diff options
Diffstat (limited to 'drivers/scsi/qla4xxx/ql4_isr.c')
| -rw-r--r-- | drivers/scsi/qla4xxx/ql4_isr.c | 1242 |
1 files changed, 1041 insertions, 201 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c index 799120fcb9b..081b6b78d2c 100644 --- a/drivers/scsi/qla4xxx/ql4_isr.c +++ b/drivers/scsi/qla4xxx/ql4_isr.c @@ -1,6 +1,6 @@ /* * QLogic iSCSI HBA Driver - * Copyright (c) 2003-2006 QLogic Corporation + * Copyright (c) 2003-2013 QLogic Corporation * * See LICENSE.qla4xxx for copyright and licensing details. */ @@ -11,6 +11,103 @@ #include "ql4_inline.h" /** + * qla4xxx_copy_sense - copy sense data into cmd sense buffer + * @ha: Pointer to host adapter structure. + * @sts_entry: Pointer to status entry structure. + * @srb: Pointer to srb structure. + **/ +static void qla4xxx_copy_sense(struct scsi_qla_host *ha, + struct status_entry *sts_entry, + struct srb *srb) +{ + struct scsi_cmnd *cmd = srb->cmd; + uint16_t sense_len; + + memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); + sense_len = le16_to_cpu(sts_entry->senseDataByteCnt); + if (sense_len == 0) { + DEBUG2(ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%d:%d: %s:" + " sense len 0\n", ha->host_no, + cmd->device->channel, cmd->device->id, + cmd->device->lun, __func__)); + ha->status_srb = NULL; + return; + } + /* Save total available sense length, + * not to exceed cmd's sense buffer size */ + sense_len = min_t(uint16_t, sense_len, SCSI_SENSE_BUFFERSIZE); + srb->req_sense_ptr = cmd->sense_buffer; + srb->req_sense_len = sense_len; + + /* Copy sense from sts_entry pkt */ + sense_len = min_t(uint16_t, sense_len, IOCB_MAX_SENSEDATA_LEN); + memcpy(cmd->sense_buffer, sts_entry->senseData, sense_len); + + DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%d: %s: sense key = %x, " + "ASL= %02x, 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[7], + sts_entry->senseData[12], + sts_entry->senseData[13])); + + DEBUG5(qla4xxx_dump_buffer(cmd->sense_buffer, sense_len)); + srb->flags |= SRB_GOT_SENSE; + + /* Update srb, in case a sts_cont pkt follows */ + srb->req_sense_ptr += sense_len; + srb->req_sense_len -= sense_len; + if (srb->req_sense_len != 0) + ha->status_srb = srb; + else + ha->status_srb = NULL; +} + +/** + * qla4xxx_status_cont_entry - Process a Status Continuations entry. + * @ha: SCSI driver HA context + * @sts_cont: Entry pointer + * + * Extended sense data. + */ +static void +qla4xxx_status_cont_entry(struct scsi_qla_host *ha, + struct status_cont_entry *sts_cont) +{ + struct srb *srb = ha->status_srb; + struct scsi_cmnd *cmd; + uint16_t sense_len; + + if (srb == NULL) + return; + + cmd = srb->cmd; + if (cmd == NULL) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s: Cmd already returned " + "back to OS srb=%p srb->state:%d\n", ha->host_no, + __func__, srb, srb->state)); + ha->status_srb = NULL; + return; + } + + /* Copy sense data. */ + sense_len = min_t(uint16_t, srb->req_sense_len, + IOCB_MAX_EXT_SENSEDATA_LEN); + memcpy(srb->req_sense_ptr, sts_cont->ext_sense_data, sense_len); + DEBUG5(qla4xxx_dump_buffer(srb->req_sense_ptr, sense_len)); + + srb->req_sense_ptr += sense_len; + srb->req_sense_len -= sense_len; + + /* Place command on done queue. */ + if (srb->req_sense_len == 0) { + kref_put(&srb->srb_ref, qla4xxx_srb_compl); + ha->status_srb = NULL; + } +} + +/** * qla4xxx_status_entry - processes status IOCBs * @ha: Pointer to host adapter structure. * @sts_entry: Pointer to status entry structure. @@ -23,18 +120,16 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, struct srb *srb; struct ddb_entry *ddb_entry; uint32_t residual; - uint16_t sensebytecnt; 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)); - dev_warn(&ha->pdev->dev, "%s invalid status entry:" - " handle=0x%0x\n", __func__, sts_entry->handle); - set_bit(DPC_RESET_HA, &ha->dpc_flags); + ql4_printk(KERN_WARNING, ha, "%s invalid status entry: " + "handle=0x%0x, srb=%p\n", __func__, + sts_entry->handle, srb); + if (is_qla80XX(ha)) + set_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags); + else + set_bit(DPC_RESET_HA, &ha->dpc_flags); return; } @@ -44,8 +139,8 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, "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); + ql4_printk(KERN_WARNING, ha, "Command is NULL:" + " already returned to OS (srb=%p)\n", srb); return; } @@ -92,24 +187,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, break; /* Copy Sense Data into sense buffer. */ - memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); - - sensebytecnt = le16_to_cpu(sts_entry->senseDataByteCnt); - if (sensebytecnt == 0) - break; - - memcpy(cmd->sense_buffer, sts_entry->senseData, - min_t(uint16_t, sensebytecnt, SCSI_SENSE_BUFFERSIZE)); - - 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; + qla4xxx_copy_sense(ha, sts_entry, srb); break; case SCS_INCOMPLETE: @@ -146,18 +224,18 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, * 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); + if (iscsi_is_session_online(ddb_entry->sess)) + qla4xxx_mark_device_missing(ddb_entry->sess); break; case SCS_DATA_UNDERRUN: case SCS_DATA_OVERRUN: if ((sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) || - (sts_entry->completionStatus == SCS_DATA_OVERRUN)) { - DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " "Data overrun, " - "residual = 0x%x\n", ha->host_no, + (sts_entry->completionStatus == SCS_DATA_OVERRUN)) { + DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " "Data overrun\n", + ha->host_no, cmd->device->channel, cmd->device->id, - cmd->device->lun, __func__, residual)); + cmd->device->lun, __func__)); cmd->result = DID_ERROR << 16; break; @@ -165,83 +243,87 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, scsi_set_resid(cmd, 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, SCSI_SENSE_BUFFERSIZE); + if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_UNDER) { + + /* Both the firmware and target reported UNDERRUN: + * + * MID-LAYER UNDERFLOW case: + * Some kernels do not properly detect midlayer + * underflow, so we manually check it and return + * ERROR if the minimum required data was not + * received. + * + * ALL OTHER cases: + * Fall thru to check scsi_status + */ + if (!scsi_status && (scsi_bufflen(cmd) - residual) < + cmd->underflow) { + DEBUG2(ql4_printk(KERN_INFO, ha, + "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__, + scsi_bufflen(cmd), + residual)); - sensebytecnt = - le16_to_cpu(sts_entry->senseDataByteCnt); - if (sensebytecnt == 0) + cmd->result = DID_ERROR << 16; break; + } - memcpy(cmd->sense_buffer, sts_entry->senseData, - min_t(uint16_t, sensebytecnt, SCSI_SENSE_BUFFERSIZE)); + } else if (scsi_status != SAM_STAT_TASK_SET_FULL && + scsi_status != SAM_STAT_BUSY) { - 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. + * The firmware reports UNDERRUN, but the target does + * not report it: + * + * scsi_status | host_byte device_byte + * | (19:16) (7:0) + * ============= | ========= =========== + * TASK_SET_FULL | DID_OK scsi_status + * BUSY | DID_OK scsi_status + * ALL OTHERS | DID_ERROR scsi_status + * + * Note: If scsi_status is task set full or busy, + * then this else if would fall thru to check the + * scsi_status and return DID_OK. */ - if ((sts_entry->iscsiFlags & - ISCSI_FLAG_RESIDUAL_UNDER) == 0) { - cmd->result = DID_BUS_BUSY << 16; - } else if ((scsi_bufflen(cmd) - 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 underrun1, " - "xferlen = 0x%x, " - "residual = 0x%x\n", ha->host_no, - cmd->device->channel, - cmd->device->id, - cmd->device->lun, __func__, - scsi_bufflen(cmd), residual)); - cmd->result = DID_ERROR << 16; - } else { - cmd->result = DID_OK << 16; - } + DEBUG2(ql4_printk(KERN_INFO, ha, + "scsi%ld:%d:%d:%d: %s: Dropped frame(s) detected (0x%x of 0x%x bytes).\n", + ha->host_no, + cmd->device->channel, + cmd->device->id, + cmd->device->lun, __func__, + residual, + scsi_bufflen(cmd))); + + cmd->result = DID_ERROR << 16 | scsi_status; + goto check_scsi_status; } + + cmd->result = DID_OK << 16 | scsi_status; + +check_scsi_status: + if (scsi_status == SAM_STAT_CHECK_CONDITION) + qla4xxx_copy_sense(ha, sts_entry, srb); + break; case SCS_DEVICE_LOGGED_OUT: case SCS_DEVICE_UNAVAILABLE: + DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%d: SCS_DEVICE " + "state: 0x%x\n", ha->host_no, + cmd->device->channel, cmd->device->id, + cmd->device->lun, sts_entry->completionStatus)); /* * 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); + if (iscsi_is_session_online(ddb_entry->sess)) + qla4xxx_mark_device_missing(ddb_entry->sess); cmd->result = DID_TRANSPORT_DISRUPTED << 16; break; @@ -268,9 +350,118 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, status_entry_exit: - /* complete the request */ + /* complete the request, if not waiting for status_continuation pkt */ srb->cc_stat = sts_entry->completionStatus; - qla4xxx_srb_compl(ha, srb); + if (ha->status_srb == NULL) + kref_put(&srb->srb_ref, qla4xxx_srb_compl); +} + +/** + * qla4xxx_passthru_status_entry - processes passthru status IOCBs (0x3C) + * @ha: Pointer to host adapter structure. + * @sts_entry: Pointer to status entry structure. + **/ +static void qla4xxx_passthru_status_entry(struct scsi_qla_host *ha, + struct passthru_status *sts_entry) +{ + struct iscsi_task *task; + struct ddb_entry *ddb_entry; + struct ql4_task_data *task_data; + struct iscsi_cls_conn *cls_conn; + struct iscsi_conn *conn; + itt_t itt; + uint32_t fw_ddb_index; + + itt = sts_entry->handle; + fw_ddb_index = le32_to_cpu(sts_entry->target); + + ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, fw_ddb_index); + + if (ddb_entry == NULL) { + ql4_printk(KERN_ERR, ha, "%s: Invalid target index = 0x%x\n", + __func__, sts_entry->target); + return; + } + + cls_conn = ddb_entry->conn; + conn = cls_conn->dd_data; + spin_lock(&conn->session->back_lock); + task = iscsi_itt_to_task(conn, itt); + spin_unlock(&conn->session->back_lock); + + if (task == NULL) { + ql4_printk(KERN_ERR, ha, "%s: Task is NULL\n", __func__); + return; + } + + task_data = task->dd_data; + memcpy(&task_data->sts, sts_entry, sizeof(struct passthru_status)); + ha->iocb_cnt -= task_data->iocb_req_cnt; + queue_work(ha->task_wq, &task_data->task_work); +} + +static struct mrb *qla4xxx_del_mrb_from_active_array(struct scsi_qla_host *ha, + uint32_t index) +{ + struct mrb *mrb = NULL; + + /* validate handle and remove from active array */ + if (index >= MAX_MRB) + return mrb; + + mrb = ha->active_mrb_array[index]; + ha->active_mrb_array[index] = NULL; + if (!mrb) + return mrb; + + /* update counters */ + ha->iocb_cnt -= mrb->iocb_cnt; + + return mrb; +} + +static void qla4xxx_mbox_status_entry(struct scsi_qla_host *ha, + struct mbox_status_iocb *mbox_sts_entry) +{ + struct mrb *mrb; + uint32_t status; + uint32_t data_size; + + mrb = qla4xxx_del_mrb_from_active_array(ha, + le32_to_cpu(mbox_sts_entry->handle)); + + if (mrb == NULL) { + ql4_printk(KERN_WARNING, ha, "%s: mrb[%d] is null\n", __func__, + mbox_sts_entry->handle); + return; + } + + switch (mrb->mbox_cmd) { + case MBOX_CMD_PING: + DEBUG2(ql4_printk(KERN_INFO, ha, "%s: mbox_cmd = 0x%x, " + "mbox_sts[0] = 0x%x, mbox_sts[6] = 0x%x\n", + __func__, mrb->mbox_cmd, + mbox_sts_entry->out_mbox[0], + mbox_sts_entry->out_mbox[6])); + + if (mbox_sts_entry->out_mbox[0] == MBOX_STS_COMMAND_COMPLETE) + status = ISCSI_PING_SUCCESS; + else + status = mbox_sts_entry->out_mbox[6]; + + data_size = sizeof(mbox_sts_entry->out_mbox); + + qla4xxx_post_ping_evt_work(ha, status, mrb->pid, data_size, + (uint8_t *) mbox_sts_entry->out_mbox); + break; + + default: + DEBUG2(ql4_printk(KERN_WARNING, ha, "%s: invalid mbox_cmd = " + "0x%x\n", __func__, mrb->mbox_cmd)); + } + + kfree(mrb); + return; } /** @@ -280,16 +471,14 @@ status_entry_exit: * 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) +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) { + while ((ha->response_ptr->signature != RESPONSE_PROCESSED)) { sts_entry = (struct status_entry *) ha->response_ptr; count++; @@ -305,20 +494,24 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha) /* process entry */ switch (sts_entry->hdr.entryType) { case ET_STATUS: - /* - * Common status - Single completion posted in single - * IOSB. - */ + /* Common status */ qla4xxx_status_entry(ha, sts_entry); break; case ET_PASSTHRU_STATUS: + if (sts_entry->hdr.systemDefined == SD_ISCSI_PDU) + qla4xxx_passthru_status_entry(ha, + (struct passthru_status *)sts_entry); + else + ql4_printk(KERN_ERR, ha, + "%s: Invalid status received\n", + __func__); + 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__)); + qla4xxx_status_cont_entry(ha, + (struct status_cont_entry *) sts_entry); break; case ET_COMMAND: @@ -338,7 +531,7 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha) /* ETRY normally by sending it back with * DID_BUS_BUSY */ srb->cmd->result = DID_BUS_BUSY << 16; - qla4xxx_srb_compl(ha, srb); + kref_put(&srb->srb_ref, qla4xxx_srb_compl); break; case ET_CONTINUE: @@ -347,6 +540,13 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha) "ignoring\n", ha->host_no, __func__)); break; + case ET_MBOX_STATUS: + DEBUG2(ql4_printk(KERN_INFO, ha, + "%s: mbox status IOCB\n", __func__)); + qla4xxx_mbox_status_entry(ha, + (struct mbox_status_iocb *)sts_entry); + break; + default: /* * Invalid entry in response queue, reset RISC @@ -358,14 +558,14 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha) sts_entry->hdr.entryType)); goto exit_prq_error; } + ((struct response *)sts_entry)->signature = RESPONSE_PROCESSED; + wmb(); } /* - * Done with responses, update the ISP For QLA4010, this also clears - * the interrupt. + * Tell ISP we're done with response(s). This also clears the interrupt. */ - writel(ha->response_out, &ha->reg->rsp_q_out); - readl(&ha->reg->rsp_q_out); + ha->isp_ops->complete_iocb(ha); return; @@ -375,13 +575,80 @@ exit_prq_invalid_handle: sts_entry->completionStatus)); exit_prq_error: - writel(ha->response_out, &ha->reg->rsp_q_out); - readl(&ha->reg->rsp_q_out); - + ha->isp_ops->complete_iocb(ha); set_bit(DPC_RESET_HA, &ha->dpc_flags); } /** + * qla4_83xx_loopback_in_progress: Is loopback in progress? + * @ha: Pointer to host adapter structure. + * @ret: 1 = loopback in progress, 0 = loopback not in progress + **/ +static int qla4_83xx_loopback_in_progress(struct scsi_qla_host *ha) +{ + int rval = 1; + + if (is_qla8032(ha) || is_qla8042(ha)) { + if ((ha->idc_info.info2 & ENABLE_INTERNAL_LOOPBACK) || + (ha->idc_info.info2 & ENABLE_EXTERNAL_LOOPBACK)) { + DEBUG2(ql4_printk(KERN_INFO, ha, + "%s: Loopback diagnostics in progress\n", + __func__)); + rval = 1; + } else { + DEBUG2(ql4_printk(KERN_INFO, ha, + "%s: Loopback diagnostics not in progress\n", + __func__)); + rval = 0; + } + } + + return rval; +} + +static void qla4xxx_update_ipaddr_state(struct scsi_qla_host *ha, + uint32_t ipaddr_idx, + uint32_t ipaddr_fw_state) +{ + uint8_t ipaddr_state; + uint8_t ip_idx; + + ip_idx = ipaddr_idx & 0xF; + ipaddr_state = qla4xxx_set_ipaddr_state((uint8_t)ipaddr_fw_state); + + switch (ip_idx) { + case 0: + ha->ip_config.ipv4_addr_state = ipaddr_state; + break; + case 1: + ha->ip_config.ipv6_link_local_state = ipaddr_state; + break; + case 2: + ha->ip_config.ipv6_addr0_state = ipaddr_state; + break; + case 3: + ha->ip_config.ipv6_addr1_state = ipaddr_state; + break; + default: + ql4_printk(KERN_INFO, ha, "%s: Invalid IPADDR index %d\n", + __func__, ip_idx); + } +} + +static void qla4xxx_default_router_changed(struct scsi_qla_host *ha, + uint32_t *mbox_sts) +{ + memcpy(&ha->ip_config.ipv6_default_router_addr.s6_addr32[0], + &mbox_sts[2], sizeof(uint32_t)); + memcpy(&ha->ip_config.ipv6_default_router_addr.s6_addr32[1], + &mbox_sts[3], sizeof(uint32_t)); + memcpy(&ha->ip_config.ipv6_default_router_addr.s6_addr32[2], + &mbox_sts[4], sizeof(uint32_t)); + memcpy(&ha->ip_config.ipv6_default_router_addr.s6_addr32[3], + &mbox_sts[5], sizeof(uint32_t)); +} + +/** * qla4xxx_isr_decode_mailbox - decodes mailbox status * @ha: Pointer to host adapter structure. * @mailbox_status: Mailbox status. @@ -393,7 +660,16 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, uint32_t mbox_status) { int i; - uint32_t mbox_stat2, mbox_stat3; + uint32_t mbox_sts[MBOX_AEN_REG_COUNT]; + __le32 __iomem *mailbox_out; + uint32_t opcode = 0; + + if (is_qla8032(ha) || is_qla8042(ha)) + mailbox_out = &ha->qla4_83xx_reg->mailbox_out[0]; + else if (is_qla8022(ha)) + mailbox_out = &ha->qla4_82xx_reg->mailbox_out[0]; + else + mailbox_out = &ha->reg->mailbox[0]; if ((mbox_status == MBOX_STS_BUSY) || (mbox_status == MBOX_STS_INTERMEDIATE_COMPLETION) || @@ -405,27 +681,37 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, * 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]); + for (i = 0; i < ha->mbox_status_count; i++) + ha->mbox_status[i] = readl(&mailbox_out[i]); set_bit(AF_MBOX_COMMAND_DONE, &ha->flags); + + if (test_bit(AF_MBOX_COMMAND_NOPOLL, &ha->flags)) + complete(&ha->mbx_intr_comp); } } else if (mbox_status >> 12 == MBOX_ASYNC_EVENT_STATUS) { + for (i = 0; i < MBOX_AEN_REG_COUNT; i++) + mbox_sts[i] = readl(&mailbox_out[i]); + /* Immediately process the AENs that don't require much work. * Only queue the database_changed AENs */ if (ha->aen_log.count < MAX_AEN_ENTRIES) { for (i = 0; i < MBOX_AEN_REG_COUNT; i++) ha->aen_log.entry[ha->aen_log.count].mbox_sts[i] = - readl(&ha->reg->mailbox[i]); + mbox_sts[i]; ha->aen_log.count++; } switch (mbox_status) { case MBOX_ASTS_SYSTEM_ERROR: /* Log Mailbox registers */ - if (ql4xdontresethba) { - DEBUG2(printk("%s:Dont Reset HBA\n", - __func__)); + ql4_printk(KERN_INFO, ha, "%s: System Err\n", __func__); + qla4xxx_dump_registers(ha); + + if ((is_qla8022(ha) && ql4xdontresethba) || + ((is_qla8032(ha) || is_qla8042(ha)) && + qla4_83xx_idc_dontreset(ha))) { + DEBUG2(printk("scsi%ld: %s:Don't Reset HBA\n", + ha->host_no, __func__)); } else { set_bit(AF_GET_CRASH_RECORD, &ha->flags); set_bit(DPC_RESET_HA, &ha->dpc_flags); @@ -439,19 +725,40 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, 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); + if (is_qla80XX(ha)) + set_bit(DPC_RESET_HA_FW_CONTEXT, + &ha->dpc_flags); + else + 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); + if (test_bit(AF_INIT_DONE, &ha->flags)) + set_bit(DPC_LINK_CHANGED, &ha->dpc_flags); + + ql4_printk(KERN_INFO, ha, "%s: LINK UP\n", __func__); + qla4xxx_post_aen_work(ha, ISCSI_EVENT_LINKUP, + sizeof(mbox_sts), + (uint8_t *) mbox_sts); + + if ((is_qla8032(ha) || is_qla8042(ha)) && + ha->notify_link_up_comp) + complete(&ha->link_up_comp); + 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); + if (test_bit(AF_INIT_DONE, &ha->flags)) { + set_bit(DPC_LINK_CHANGED, &ha->dpc_flags); + qla4xxx_wake_dpc(ha); + } + + ql4_printk(KERN_INFO, ha, "%s: LINK DOWN\n", __func__); + qla4xxx_post_aen_work(ha, ISCSI_EVENT_LINKDOWN, + sizeof(mbox_sts), + (uint8_t *) mbox_sts); break; case MBOX_ASTS_HEARTBEAT: @@ -471,19 +778,57 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, case MBOX_ASTS_UNSOLICITED_PDU_RECEIVED: /* Connection mode */ case MBOX_ASTS_IPSEC_SYSTEM_FATAL_ERROR: case MBOX_ASTS_SUBNET_STATE_CHANGE: + case MBOX_ASTS_DUPLICATE_IP: /* No action */ DEBUG2(printk("scsi%ld: AEN %04x\n", ha->host_no, mbox_status)); break; case MBOX_ASTS_IP_ADDR_STATE_CHANGED: - mbox_stat2 = readl(&ha->reg->mailbox[2]); - mbox_stat3 = readl(&ha->reg->mailbox[3]); - - if ((mbox_stat3 == 5) && (mbox_stat2 == 3)) + printk("scsi%ld: AEN %04x, mbox_sts[2]=%04x, " + "mbox_sts[3]=%04x\n", ha->host_no, mbox_sts[0], + mbox_sts[2], mbox_sts[3]); + + qla4xxx_update_ipaddr_state(ha, mbox_sts[5], + mbox_sts[3]); + /* mbox_sts[2] = Old ACB state + * mbox_sts[3] = new ACB state */ + if ((mbox_sts[3] == IP_ADDRSTATE_PREFERRED) && + ((mbox_sts[2] == IP_ADDRSTATE_TENTATIVE) || + (mbox_sts[2] == IP_ADDRSTATE_ACQUIRING))) { set_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags); - else if ((mbox_stat3 == 2) && (mbox_stat2 == 5)) - set_bit(DPC_RESET_HA, &ha->dpc_flags); + } else if ((mbox_sts[3] == IP_ADDRSTATE_ACQUIRING) && + (mbox_sts[2] == IP_ADDRSTATE_PREFERRED)) { + if (is_qla80XX(ha)) + set_bit(DPC_RESET_HA_FW_CONTEXT, + &ha->dpc_flags); + else + set_bit(DPC_RESET_HA, &ha->dpc_flags); + } else if (mbox_sts[3] == IP_ADDRSTATE_DISABLING) { + ql4_printk(KERN_INFO, ha, "scsi%ld: %s: ACB in disabling state\n", + ha->host_no, __func__); + } else if (mbox_sts[3] == IP_ADDRSTATE_UNCONFIGURED) { + complete(&ha->disable_acb_comp); + ql4_printk(KERN_INFO, ha, "scsi%ld: %s: ACB state unconfigured\n", + ha->host_no, __func__); + } + break; + + case MBOX_ASTS_IPV6_LINK_MTU_CHANGE: + case MBOX_ASTS_IPV6_AUTO_PREFIX_IGNORED: + case MBOX_ASTS_IPV6_ND_LOCAL_PREFIX_IGNORED: + /* No action */ + DEBUG2(ql4_printk(KERN_INFO, ha, "scsi%ld: AEN %04x\n", + ha->host_no, mbox_status)); + break; + + case MBOX_ASTS_ICMPV6_ERROR_MSG_RCVD: + DEBUG2(ql4_printk(KERN_INFO, ha, + "scsi%ld: AEN %04x, IPv6 ERROR, " + "mbox_sts[1]=%08x, mbox_sts[2]=%08x, mbox_sts[3}=%08x, mbox_sts[4]=%08x mbox_sts[5]=%08x\n", + ha->host_no, mbox_sts[0], mbox_sts[1], + mbox_sts[2], mbox_sts[3], mbox_sts[4], + mbox_sts[5])); break; case MBOX_ASTS_MAC_ADDRESS_CHANGED: @@ -491,9 +836,8 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, /* 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]))); + ha->host_no, mbox_sts[0], + mbox_sts[1], mbox_sts[2])); break; case MBOX_ASTS_SELF_TEST_FAILED: @@ -501,10 +845,8 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, /* 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]))); + ha->host_no, mbox_sts[0], mbox_sts[1], + mbox_sts[2], mbox_sts[3])); break; case MBOX_ASTS_DATABASE_CHANGED: @@ -515,21 +857,19 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, /* decrement available counter */ ha->aen_q_count--; - for (i = 1; i < MBOX_AEN_REG_COUNT; i++) + for (i = 0; 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; + mbox_sts[i]; /* print debug message */ - DEBUG2(printk("scsi%ld: AEN[%d] %04x queued" - " mb1:0x%x mb2:0x%x mb3:0x%x mb4:0x%x\n", + DEBUG2(printk("scsi%ld: AEN[%d] %04x queued " + "mb1:0x%x mb2:0x%x mb3:0x%x " + "mb4:0x%x mb5: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])); + mbox_sts[0], mbox_sts[1], + mbox_sts[2], mbox_sts[3], + mbox_sts[4], mbox_sts[5])); + /* advance pointer */ ha->aen_in++; if (ha->aen_in == MAX_AEN_ENTRIES) @@ -541,26 +881,139 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, DEBUG2(printk("scsi%ld: %s: aen %04x, queue " "overflowed! AEN LOST!!\n", ha->host_no, __func__, - mbox_status)); + mbox_sts[0])); 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])); + "%04x\n", i, mbox_sts[0], + mbox_sts[1], mbox_sts[2], + mbox_sts[3])); } } break; + case MBOX_ASTS_TXSCVR_INSERTED: + DEBUG2(printk(KERN_WARNING + "scsi%ld: AEN %04x Transceiver" + " inserted\n", ha->host_no, mbox_sts[0])); + break; + + case MBOX_ASTS_TXSCVR_REMOVED: + DEBUG2(printk(KERN_WARNING + "scsi%ld: AEN %04x Transceiver" + " removed\n", ha->host_no, mbox_sts[0])); + break; + + case MBOX_ASTS_IDC_REQUEST_NOTIFICATION: + if (is_qla8032(ha) || is_qla8042(ha)) { + DEBUG2(ql4_printk(KERN_INFO, ha, + "scsi%ld: AEN %04x, mbox_sts[1]=%08x, mbox_sts[2]=%08x, mbox_sts[3]=%08x, mbox_sts[4]=%08x\n", + ha->host_no, mbox_sts[0], + mbox_sts[1], mbox_sts[2], + mbox_sts[3], mbox_sts[4])); + opcode = mbox_sts[1] >> 16; + if ((opcode == MBOX_CMD_SET_PORT_CONFIG) || + (opcode == MBOX_CMD_PORT_RESET)) { + set_bit(DPC_POST_IDC_ACK, + &ha->dpc_flags); + ha->idc_info.request_desc = mbox_sts[1]; + ha->idc_info.info1 = mbox_sts[2]; + ha->idc_info.info2 = mbox_sts[3]; + ha->idc_info.info3 = mbox_sts[4]; + qla4xxx_wake_dpc(ha); + } + } + break; + + case MBOX_ASTS_IDC_COMPLETE: + if (is_qla8032(ha) || is_qla8042(ha)) { + DEBUG2(ql4_printk(KERN_INFO, ha, + "scsi%ld: AEN %04x, mbox_sts[1]=%08x, mbox_sts[2]=%08x, mbox_sts[3]=%08x, mbox_sts[4]=%08x\n", + ha->host_no, mbox_sts[0], + mbox_sts[1], mbox_sts[2], + mbox_sts[3], mbox_sts[4])); + DEBUG2(ql4_printk(KERN_INFO, ha, + "scsi:%ld: AEN %04x IDC Complete notification\n", + ha->host_no, mbox_sts[0])); + + opcode = mbox_sts[1] >> 16; + if (ha->notify_idc_comp) + complete(&ha->idc_comp); + + if ((opcode == MBOX_CMD_SET_PORT_CONFIG) || + (opcode == MBOX_CMD_PORT_RESET)) + ha->idc_info.info2 = mbox_sts[3]; + + if (qla4_83xx_loopback_in_progress(ha)) { + set_bit(AF_LOOPBACK, &ha->flags); + } else { + clear_bit(AF_LOOPBACK, &ha->flags); + if (ha->saved_acb) + set_bit(DPC_RESTORE_ACB, + &ha->dpc_flags); + } + qla4xxx_wake_dpc(ha); + } + break; + + case MBOX_ASTS_IPV6_DEFAULT_ROUTER_CHANGED: + DEBUG2(ql4_printk(KERN_INFO, ha, + "scsi%ld: AEN %04x, mbox_sts[1]=%08x, mbox_sts[2]=%08x, mbox_sts[3]=%08x, mbox_sts[4]=%08x mbox_sts[5]=%08x\n", + ha->host_no, mbox_sts[0], mbox_sts[1], + mbox_sts[2], mbox_sts[3], mbox_sts[4], + mbox_sts[5])); + DEBUG2(ql4_printk(KERN_INFO, ha, + "scsi%ld: AEN %04x Received IPv6 default router changed notification\n", + ha->host_no, mbox_sts[0])); + qla4xxx_default_router_changed(ha, mbox_sts); + break; + + case MBOX_ASTS_IDC_TIME_EXTEND_NOTIFICATION: + DEBUG2(ql4_printk(KERN_INFO, ha, + "scsi%ld: AEN %04x, mbox_sts[1]=%08x, mbox_sts[2]=%08x, mbox_sts[3]=%08x, mbox_sts[4]=%08x mbox_sts[5]=%08x\n", + ha->host_no, mbox_sts[0], mbox_sts[1], + mbox_sts[2], mbox_sts[3], mbox_sts[4], + mbox_sts[5])); + DEBUG2(ql4_printk(KERN_INFO, ha, + "scsi%ld: AEN %04x Received IDC Extend Timeout notification\n", + ha->host_no, mbox_sts[0])); + /* new IDC timeout */ + ha->idc_extend_tmo = mbox_sts[1]; + break; + + case MBOX_ASTS_INITIALIZATION_FAILED: + DEBUG2(ql4_printk(KERN_INFO, ha, + "scsi%ld: AEN %04x, mbox_sts[3]=%08x\n", + ha->host_no, mbox_sts[0], + mbox_sts[3])); + break; + + case MBOX_ASTS_SYSTEM_WARNING_EVENT: + DEBUG2(ql4_printk(KERN_WARNING, ha, + "scsi%ld: AEN %04x, mbox_sts[1]=%08x, mbox_sts[2]=%08x, mbox_sts[3]=%08x, mbox_sts[4]=%08x mbox_sts[5]=%08x\n", + ha->host_no, mbox_sts[0], mbox_sts[1], + mbox_sts[2], mbox_sts[3], mbox_sts[4], + mbox_sts[5])); + break; + + case MBOX_ASTS_DCBX_CONF_CHANGE: + DEBUG2(ql4_printk(KERN_INFO, ha, + "scsi%ld: AEN %04x, mbox_sts[1]=%08x, mbox_sts[2]=%08x, mbox_sts[3]=%08x, mbox_sts[4]=%08x mbox_sts[5]=%08x\n", + ha->host_no, mbox_sts[0], mbox_sts[1], + mbox_sts[2], mbox_sts[3], mbox_sts[4], + mbox_sts[5])); + DEBUG2(ql4_printk(KERN_INFO, ha, + "scsi%ld: AEN %04x Received DCBX configuration changed notification\n", + ha->host_no, mbox_sts[0])); + break; + default: DEBUG2(printk(KERN_WARNING "scsi%ld: AEN %04x UNKNOWN\n", - ha->host_no, mbox_status)); + ha->host_no, mbox_sts[0])); break; } } else { @@ -571,6 +1024,48 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, } } +void qla4_83xx_interrupt_service_routine(struct scsi_qla_host *ha, + uint32_t intr_status) +{ + /* Process mailbox/asynch event interrupt.*/ + if (intr_status) { + qla4xxx_isr_decode_mailbox(ha, + readl(&ha->qla4_83xx_reg->mailbox_out[0])); + /* clear the interrupt */ + writel(0, &ha->qla4_83xx_reg->risc_intr); + } else { + qla4xxx_process_response_queue(ha); + } + + /* clear the interrupt */ + writel(0, &ha->qla4_83xx_reg->mb_int_mask); +} + +/** + * qla4_82xx_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 qla4_82xx_interrupt_service_routine(struct scsi_qla_host *ha, + uint32_t intr_status) +{ + /* Process response queue interrupt. */ + if ((intr_status & HSRX_RISC_IOCB_INT) && + test_bit(AF_INIT_DONE, &ha->flags)) + qla4xxx_process_response_queue(ha); + + /* Process mailbox/asynch event interrupt.*/ + if (intr_status & HSRX_RISC_MB_INT) + qla4xxx_isr_decode_mailbox(ha, + readl(&ha->qla4_82xx_reg->mailbox_out[0])); + + /* clear the interrupt */ + writel(0, &ha->qla4_82xx_reg->host_int); + readl(&ha->qla4_82xx_reg->host_int); +} + /** * qla4xxx_interrupt_service_routine - isr * @ha: pointer to host adapter structure. @@ -598,6 +1093,28 @@ void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha, } /** + * qla4_82xx_spurious_interrupt - processes spurious interrupt + * @ha: pointer to host adapter structure. + * @reqs_count: . + * + **/ +static void qla4_82xx_spurious_interrupt(struct scsi_qla_host *ha, + uint8_t reqs_count) +{ + if (reqs_count) + return; + + DEBUG2(ql4_printk(KERN_INFO, ha, "Spurious Interrupt\n")); + if (is_qla8022(ha)) { + writel(0, &ha->qla4_82xx_reg->host_int); + if (test_bit(AF_INTx_ENABLED, &ha->flags)) + qla4_82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, + 0xfbff); + } + ha->spurious_int_count++; +} + +/** * qla4xxx_intr_handler - hardware interrupt handler. * @irq: Unused * @dev_id: Pointer to host adapter structure @@ -627,15 +1144,14 @@ irqreturn_t qla4xxx_intr_handler(int irq, void *dev_id) /* * Read interrupt status */ - if (le32_to_cpu(ha->shadow_regs->rsp_q_in) != + if (ha->isp_ops->rd_shdw_rsp_q_in(ha) != 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) { + (CSR_SCSI_RESET_INTR|CSR_FATAL_ERROR|INTR_PENDING)) == 0) { if (reqs_count == 0) ha->spurious_int_count++; break; @@ -677,22 +1193,270 @@ irqreturn_t qla4xxx_intr_handler(int irq, void *dev_id) &ha->reg->ctrl_status); readl(&ha->reg->ctrl_status); - if (!ql4_mod_unload) + if (!test_bit(AF_HA_REMOVAL, &ha->flags)) set_bit(DPC_RESET_HA_INTR, &ha->dpc_flags); break; } else if (intr_status & INTR_PENDING) { - qla4xxx_interrupt_service_routine(ha, intr_status); + ha->isp_ops->interrupt_service_routine(ha, intr_status); ha->total_io_count++; if (++reqs_count == MAX_REQS_SERVICED_PER_INTR) break; + } + } + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return IRQ_HANDLED; +} - intr_status = 0; +/** + * qla4_82xx_intr_handler - hardware interrupt handler. + * @irq: Unused + * @dev_id: Pointer to host adapter structure + **/ +irqreturn_t qla4_82xx_intr_handler(int irq, void *dev_id) +{ + struct scsi_qla_host *ha = dev_id; + uint32_t intr_status; + uint32_t status; + unsigned long flags = 0; + uint8_t reqs_count = 0; + + if (unlikely(pci_channel_offline(ha->pdev))) + return IRQ_HANDLED; + + ha->isr_count++; + status = qla4_82xx_rd_32(ha, ISR_INT_VECTOR); + if (!(status & ha->nx_legacy_intr.int_vec_bit)) + return IRQ_NONE; + + status = qla4_82xx_rd_32(ha, ISR_INT_STATE_REG); + if (!ISR_IS_LEGACY_INTR_TRIGGERED(status)) { + DEBUG7(ql4_printk(KERN_INFO, ha, + "%s legacy Int not triggered\n", __func__)); + return IRQ_NONE; + } + + /* clear the interrupt */ + qla4_82xx_wr_32(ha, ha->nx_legacy_intr.tgt_status_reg, 0xffffffff); + + /* read twice to ensure write is flushed */ + qla4_82xx_rd_32(ha, ISR_INT_VECTOR); + qla4_82xx_rd_32(ha, ISR_INT_VECTOR); + + spin_lock_irqsave(&ha->hardware_lock, flags); + while (1) { + if (!(readl(&ha->qla4_82xx_reg->host_int) & + ISRX_82XX_RISC_INT)) { + qla4_82xx_spurious_interrupt(ha, reqs_count); + break; } + intr_status = readl(&ha->qla4_82xx_reg->host_status); + if ((intr_status & + (HSRX_RISC_MB_INT | HSRX_RISC_IOCB_INT)) == 0) { + qla4_82xx_spurious_interrupt(ha, reqs_count); + break; + } + + ha->isp_ops->interrupt_service_routine(ha, intr_status); + + /* Enable Interrupt */ + qla4_82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0xfbff); + + if (++reqs_count == MAX_REQS_SERVICED_PER_INTR) + break; + } + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return IRQ_HANDLED; +} + +#define LEG_INT_PTR_B31 (1 << 31) +#define LEG_INT_PTR_B30 (1 << 30) +#define PF_BITS_MASK (0xF << 16) + +/** + * qla4_83xx_intr_handler - hardware interrupt handler. + * @irq: Unused + * @dev_id: Pointer to host adapter structure + **/ +irqreturn_t qla4_83xx_intr_handler(int irq, void *dev_id) +{ + struct scsi_qla_host *ha = dev_id; + uint32_t leg_int_ptr = 0; + unsigned long flags = 0; + + ha->isr_count++; + leg_int_ptr = readl(&ha->qla4_83xx_reg->leg_int_ptr); + + /* Legacy interrupt is valid if bit31 of leg_int_ptr is set */ + if (!(leg_int_ptr & LEG_INT_PTR_B31)) { + DEBUG7(ql4_printk(KERN_ERR, ha, + "%s: Legacy Interrupt Bit 31 not set, spurious interrupt!\n", + __func__)); + return IRQ_NONE; + } + + /* Validate the PCIE function ID set in leg_int_ptr bits [19..16] */ + if ((leg_int_ptr & PF_BITS_MASK) != ha->pf_bit) { + DEBUG7(ql4_printk(KERN_ERR, ha, + "%s: Incorrect function ID 0x%x in legacy interrupt register, ha->pf_bit = 0x%x\n", + __func__, (leg_int_ptr & PF_BITS_MASK), + ha->pf_bit)); + return IRQ_NONE; + } + + /* To de-assert legacy interrupt, write 0 to Legacy Interrupt Trigger + * Control register and poll till Legacy Interrupt Pointer register + * bit30 is 0. + */ + writel(0, &ha->qla4_83xx_reg->leg_int_trig); + do { + leg_int_ptr = readl(&ha->qla4_83xx_reg->leg_int_ptr); + if ((leg_int_ptr & PF_BITS_MASK) != ha->pf_bit) + break; + } while (leg_int_ptr & LEG_INT_PTR_B30); + + spin_lock_irqsave(&ha->hardware_lock, flags); + leg_int_ptr = readl(&ha->qla4_83xx_reg->risc_intr); + ha->isp_ops->interrupt_service_routine(ha, leg_int_ptr); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return IRQ_HANDLED; +} + +irqreturn_t +qla4_8xxx_msi_handler(int irq, void *dev_id) +{ + struct scsi_qla_host *ha; + + ha = (struct scsi_qla_host *) dev_id; + if (!ha) { + DEBUG2(printk(KERN_INFO + "qla4xxx: MSIX: Interrupt with NULL host ptr\n")); + return IRQ_NONE; } + ha->isr_count++; + /* clear the interrupt */ + qla4_82xx_wr_32(ha, ha->nx_legacy_intr.tgt_status_reg, 0xffffffff); + + /* read twice to ensure write is flushed */ + qla4_82xx_rd_32(ha, ISR_INT_VECTOR); + qla4_82xx_rd_32(ha, ISR_INT_VECTOR); + + return qla4_8xxx_default_intr_handler(irq, dev_id); +} + +static irqreturn_t qla4_83xx_mailbox_intr_handler(int irq, void *dev_id) +{ + struct scsi_qla_host *ha = dev_id; + unsigned long flags; + uint32_t ival = 0; + + spin_lock_irqsave(&ha->hardware_lock, flags); + + ival = readl(&ha->qla4_83xx_reg->risc_intr); + if (ival == 0) { + ql4_printk(KERN_INFO, ha, + "%s: It is a spurious mailbox interrupt!\n", + __func__); + ival = readl(&ha->qla4_83xx_reg->mb_int_mask); + ival &= ~INT_MASK_FW_MB; + writel(ival, &ha->qla4_83xx_reg->mb_int_mask); + goto exit; + } + + qla4xxx_isr_decode_mailbox(ha, + readl(&ha->qla4_83xx_reg->mailbox_out[0])); + writel(0, &ha->qla4_83xx_reg->risc_intr); + ival = readl(&ha->qla4_83xx_reg->mb_int_mask); + ival &= ~INT_MASK_FW_MB; + writel(ival, &ha->qla4_83xx_reg->mb_int_mask); + ha->isr_count++; +exit: spin_unlock_irqrestore(&ha->hardware_lock, flags); + return IRQ_HANDLED; +} + +/** + * qla4_8xxx_default_intr_handler - hardware interrupt handler. + * @irq: Unused + * @dev_id: Pointer to host adapter structure + * + * This interrupt handler is called directly for MSI-X, and + * called indirectly for MSI. + **/ +irqreturn_t +qla4_8xxx_default_intr_handler(int irq, void *dev_id) +{ + struct scsi_qla_host *ha = dev_id; + unsigned long flags; + uint32_t intr_status; + uint8_t reqs_count = 0; + if (is_qla8032(ha) || is_qla8042(ha)) { + qla4_83xx_mailbox_intr_handler(irq, dev_id); + } else { + spin_lock_irqsave(&ha->hardware_lock, flags); + while (1) { + if (!(readl(&ha->qla4_82xx_reg->host_int) & + ISRX_82XX_RISC_INT)) { + qla4_82xx_spurious_interrupt(ha, reqs_count); + break; + } + + intr_status = readl(&ha->qla4_82xx_reg->host_status); + if ((intr_status & + (HSRX_RISC_MB_INT | HSRX_RISC_IOCB_INT)) == 0) { + qla4_82xx_spurious_interrupt(ha, reqs_count); + break; + } + + ha->isp_ops->interrupt_service_routine(ha, intr_status); + + if (++reqs_count == MAX_REQS_SERVICED_PER_INTR) + break; + } + ha->isr_count++; + spin_unlock_irqrestore(&ha->hardware_lock, flags); + } + return IRQ_HANDLED; +} + +irqreturn_t +qla4_8xxx_msix_rsp_q(int irq, void *dev_id) +{ + struct scsi_qla_host *ha = dev_id; + unsigned long flags; + int intr_status; + uint32_t ival = 0; + + spin_lock_irqsave(&ha->hardware_lock, flags); + if (is_qla8032(ha) || is_qla8042(ha)) { + ival = readl(&ha->qla4_83xx_reg->iocb_int_mask); + if (ival == 0) { + ql4_printk(KERN_INFO, ha, "%s: It is a spurious iocb interrupt!\n", + __func__); + goto exit_msix_rsp_q; + } + qla4xxx_process_response_queue(ha); + writel(0, &ha->qla4_83xx_reg->iocb_int_mask); + } else { + intr_status = readl(&ha->qla4_82xx_reg->host_status); + if (intr_status & HSRX_RISC_IOCB_INT) { + qla4xxx_process_response_queue(ha); + writel(0, &ha->qla4_82xx_reg->host_int); + } else { + ql4_printk(KERN_INFO, ha, "%s: spurious iocb interrupt...\n", + __func__); + goto exit_msix_rsp_q; + } + } + ha->isr_count++; +exit_msix_rsp_q: + spin_unlock_irqrestore(&ha->hardware_lock, flags); return IRQ_HANDLED; } @@ -737,51 +1501,127 @@ void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen) switch (mbox_sts[0]) { case MBOX_ASTS_DATABASE_CHANGED: - if (process_aen == FLUSH_DDB_CHANGED_AENS) { + switch (process_aen) { + case 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)); + case PROCESS_ALL_AENS: + default: + /* Specific device. */ + if (mbox_sts[1] == 1) + qla4xxx_process_ddb_changed(ha, + mbox_sts[2], mbox_sts[3], + mbox_sts[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); } +int qla4xxx_request_irqs(struct scsi_qla_host *ha) +{ + int ret = 0; + int rval = QLA_ERROR; + + if (is_qla40XX(ha)) + goto try_intx; + + if (ql4xenablemsix == 2) { + /* Note: MSI Interrupts not supported for ISP8324 and ISP8042 */ + if (is_qla8032(ha) || is_qla8042(ha)) { + ql4_printk(KERN_INFO, ha, "%s: MSI Interrupts not supported for ISP%04x, Falling back-to INTx mode\n", + __func__, ha->pdev->device); + goto try_intx; + } + goto try_msi; + } + + if (ql4xenablemsix == 0 || ql4xenablemsix != 1) + goto try_intx; + + /* Trying MSI-X */ + ret = qla4_8xxx_enable_msix(ha); + if (!ret) { + DEBUG2(ql4_printk(KERN_INFO, ha, + "MSI-X: Enabled (0x%X).\n", ha->revision_id)); + goto irq_attached; + } else { + if (is_qla8032(ha) || is_qla8042(ha)) { + ql4_printk(KERN_INFO, ha, "%s: ISP%04x: MSI-X: Falling back-to INTx mode. ret = %d\n", + __func__, ha->pdev->device, ret); + goto try_intx; + } + } + + ql4_printk(KERN_WARNING, ha, + "MSI-X: Falling back-to MSI mode -- %d.\n", ret); + +try_msi: + /* Trying MSI */ + ret = pci_enable_msi(ha->pdev); + if (!ret) { + ret = request_irq(ha->pdev->irq, qla4_8xxx_msi_handler, + 0, DRIVER_NAME, ha); + if (!ret) { + DEBUG2(ql4_printk(KERN_INFO, ha, "MSI: Enabled.\n")); + set_bit(AF_MSI_ENABLED, &ha->flags); + goto irq_attached; + } else { + ql4_printk(KERN_WARNING, ha, + "MSI: Failed to reserve interrupt %d " + "already in use.\n", ha->pdev->irq); + pci_disable_msi(ha->pdev); + } + } + +try_intx: + if (is_qla8022(ha)) { + ql4_printk(KERN_WARNING, ha, "%s: ISP82xx Legacy interrupt not supported\n", + __func__); + goto irq_not_attached; + } + + /* Trying INTx */ + ret = request_irq(ha->pdev->irq, ha->isp_ops->intr_handler, + IRQF_SHARED, DRIVER_NAME, ha); + if (!ret) { + DEBUG2(ql4_printk(KERN_INFO, ha, "INTx: Enabled.\n")); + set_bit(AF_INTx_ENABLED, &ha->flags); + goto irq_attached; + + } else { + ql4_printk(KERN_WARNING, ha, + "INTx: Failed to reserve interrupt %d already in" + " use.\n", ha->pdev->irq); + goto irq_not_attached; + } + +irq_attached: + set_bit(AF_IRQ_ATTACHED, &ha->flags); + ha->host->irq = ha->pdev->irq; + ql4_printk(KERN_INFO, ha, "%s: irq %d attached\n", + __func__, ha->pdev->irq); + rval = QLA_SUCCESS; +irq_not_attached: + return rval; +} + +void qla4xxx_free_irqs(struct scsi_qla_host *ha) +{ + if (test_and_clear_bit(AF_IRQ_ATTACHED, &ha->flags)) { + if (test_bit(AF_MSIX_ENABLED, &ha->flags)) { + qla4_8xxx_disable_msix(ha); + } else if (test_and_clear_bit(AF_MSI_ENABLED, &ha->flags)) { + free_irq(ha->pdev->irq, ha); + pci_disable_msi(ha->pdev); + } else if (test_and_clear_bit(AF_INTx_ENABLED, &ha->flags)) { + free_irq(ha->pdev->irq, ha); + } + } +} |
