int qla2x00_request_irqs(scsi_qla_host_t *ha) { int ret; /* If possible, enable MSI-X. */ if (!IS_QLA2432(ha)) goto skip_msix; if (ha->chip_revision < QLA_MSIX_CHIP_REV_24XX || !QLA_MSIX_FW_MODE_1(ha->fw_attributes)) { DEBUG2(qla_printk(KERN_WARNING, ha, "MSI-X: Unsupported ISP2432 (0x%X, 0x%X).\n", ha->chip_revision, ha->fw_attributes)); goto skip_msix; } ret = qla24xx_enable_msix(ha); if (!ret) { DEBUG2(qla_printk(KERN_INFO, ha, "MSI-X: Enabled (0x%X, 0x%X).\n", ha->chip_revision, ha->fw_attributes)); return ret; } qla_printk(KERN_WARNING, ha, "MSI-X: Falling back-to INTa mode -- %d.\n", ret); skip_msix: if (!IS_QLA24XX(ha)) goto skip_msi; ret = pci_enable_msi(ha->pdev); if (!ret) { DEBUG2(qla_printk(KERN_INFO, ha, "MSI: Enabled.\n")); ha->flags.msi_enabled = 1; } skip_msi: ret = request_irq(ha->pdev->irq, ha->isp_ops.intr_handler, IRQF_DISABLED|IRQF_SHARED, QLA2XXX_DRIVER_NAME, ha); if (!ret) { ha->flags.inta_enabled = 1; ha->host->irq = ha->pdev->irq; } else { qla_printk(KERN_WARNING, ha, "Failed to reserve interrupt %d already in use.\n", ha->pdev->irq); } return ret; }
/** * qla2x00_ms_entry() - Process a Management Server entry. * @ha: SCSI driver HA context * @index: Response queue out pointer */ static void qla2x00_ms_entry(scsi_qla_host_t *ha, ms_iocb_entry_t *pkt) { srb_t *sp; DEBUG3(printk("%s(%ld): pkt=%p pkthandle=%d.\n", __func__, ha->host_no, pkt, pkt->handle1)); /* Validate handle. */ if (pkt->handle1 < MAX_OUTSTANDING_COMMANDS) sp = ha->outstanding_cmds[pkt->handle1]; else sp = NULL; if (sp == NULL) { DEBUG2(printk("scsi(%ld): MS entry - invalid handle\n", ha->host_no)); qla_printk(KERN_WARNING, ha, "MS entry - invalid handle\n"); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); return; } CMD_COMPL_STATUS(sp->cmd) = le16_to_cpu(pkt->status); CMD_ENTRY_STATUS(sp->cmd) = pkt->entry_status; /* Free outstanding command slot. */ ha->outstanding_cmds[pkt->handle1] = 0; add_to_done_queue(ha, sp); }
/** * qla2x00_error_entry() - Process an error entry. * @ha: SCSI driver HA context * @pkt: Entry pointer */ static void qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt) { srb_t *sp; #if defined(QL_DEBUG_LEVEL_2) if (pkt->entry_status & RF_INV_E_ORDER) qla_printk(KERN_ERR, ha, "%s: Invalid Entry Order\n", __func__); else if (pkt->entry_status & RF_INV_E_COUNT) qla_printk(KERN_ERR, ha, "%s: Invalid Entry Count\n", __func__); else if (pkt->entry_status & RF_INV_E_PARAM) qla_printk(KERN_ERR, ha, "%s: Invalid Entry Parameter\n", __func__); else if (pkt->entry_status & RF_INV_E_TYPE) qla_printk(KERN_ERR, ha, "%s: Invalid Entry Type\n", __func__); else if (pkt->entry_status & RF_BUSY) qla_printk(KERN_ERR, ha, "%s: Busy\n", __func__); else qla_printk(KERN_ERR, ha, "%s: UNKNOWN flag error\n", __func__); #endif /* Validate handle. */ if (pkt->handle < MAX_OUTSTANDING_COMMANDS) sp = ha->outstanding_cmds[pkt->handle]; else sp = NULL; if (sp) { /* Free outstanding command slot. */ ha->outstanding_cmds[pkt->handle] = 0; if (ha->actthreads) ha->actthreads--; sp->lun_queue->out_cnt--; /* Bad payload or header */ if (pkt->entry_status & (RF_INV_E_ORDER | RF_INV_E_COUNT | RF_INV_E_PARAM | RF_INV_E_TYPE)) { sp->cmd->result = DID_ERROR << 16; } else if (pkt->entry_status & RF_BUSY) { sp->cmd->result = DID_BUS_BUSY << 16; } else { sp->cmd->result = DID_ERROR << 16; } /* Place command on done queue. */ add_to_done_queue(ha, sp); } else if (pkt->entry_type == COMMAND_A64_TYPE || pkt->entry_type == COMMAND_TYPE) { DEBUG2(printk("scsi(%ld): Error entry - invalid handle\n", ha->host_no)); qla_printk(KERN_WARNING, ha, "Error entry - invalid handle\n"); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); if (ha->dpc_wait && !ha->dpc_active) up(ha->dpc_wait); } }
/** * qla2x00_process_completed_request() - Process a Fast Post response. * @ha: SCSI driver HA context * @index: SRB index */ static void qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index) { srb_t *sp; /* Validate handle. */ if (index >= MAX_OUTSTANDING_COMMANDS) { DEBUG2(printk("scsi(%ld): Invalid SCSI completion handle %d.\n", ha->host_no, index)); qla_printk(KERN_WARNING, ha, "Invalid SCSI completion handle %d.\n", index); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); return; } sp = ha->outstanding_cmds[index]; if (sp) { /* Free outstanding command slot. */ ha->outstanding_cmds[index] = 0; if (ha->actthreads) ha->actthreads--; sp->lun_queue->out_cnt--; CMD_COMPL_STATUS(sp->cmd) = 0L; CMD_SCSI_STATUS(sp->cmd) = 0L; /* Save ISP completion status */ sp->cmd->result = DID_OK << 16; sp->fo_retry_cnt = 0; add_to_done_queue(ha, sp); } else { DEBUG2(printk("scsi(%ld): Invalid ISP SCSI completion handle\n", ha->host_no)); qla_printk(KERN_WARNING, ha, "Invalid ISP SCSI completion handle\n"); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); } }
static void qla2x00_adjust_sdev_qdepth_down(struct scsi_device *sdev, void *data) { fc_port_t *fcport = data; if (!scsi_track_queue_full(sdev, sdev->queue_depth - 1)) return; DEBUG2(qla_printk(KERN_INFO, fcport->ha, "scsi(%ld:%d:%d:%d): Queue depth adjusted-down to %d.\n", fcport->ha->host_no, sdev->channel, sdev->id, sdev->lun, sdev->queue_depth)); }
static int qla24xx_enable_msix(scsi_qla_host_t *ha) { int i, ret; struct msix_entry entries[QLA_MSIX_ENTRIES]; struct qla_msix_entry *qentry; for (i = 0; i < QLA_MSIX_ENTRIES; i++) entries[i].entry = imsix_entries[i].entry; ret = pci_enable_msix(ha->pdev, entries, ARRAY_SIZE(entries)); if (ret) { qla_printk(KERN_WARNING, ha, "MSI-X: Failed to enable support -- %d/%d\n", QLA_MSIX_ENTRIES, ret); goto msix_out; } ha->flags.msix_enabled = 1; for (i = 0; i < QLA_MSIX_ENTRIES; i++) { qentry = &ha->msix_entries[imsix_entries[i].index]; qentry->msix_vector = entries[i].vector; qentry->msix_entry = entries[i].entry; qentry->have_irq = 0; ret = request_irq(qentry->msix_vector, imsix_entries[i].handler, 0, imsix_entries[i].name, ha); if (ret) { qla_printk(KERN_WARNING, ha, "MSI-X: Unable to register handler -- %x/%d.\n", imsix_entries[i].index, ret); qla24xx_disable_msix(ha); goto msix_out; } qentry->have_irq = 1; } msix_out: return ret; }
/** * qla2x00_status_cont_entry() - Process a Status Continuations entry. * @ha: SCSI driver HA context * @pkt: Entry pointer * * Extended sense data. */ static void qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt) { uint8_t sense_sz = 0; srb_t *sp = ha->status_srb; struct scsi_cmnd *cp; if (sp != NULL && sp->request_sense_length != 0) { cp = sp->cmd; if (cp == NULL) { DEBUG2(printk("%s(): Cmd already returned back to OS " "sp=%p.\n", __func__, sp)); qla_printk(KERN_INFO, ha, "cmd is NULL: already returned to OS (sp=%p)\n", sp); ha->status_srb = NULL; return; } if (sp->request_sense_length > sizeof(pkt->data)) { sense_sz = sizeof(pkt->data); } else { sense_sz = sp->request_sense_length; } /* Move sense data. */ if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) host_to_fcp_swap(pkt->data, sizeof(pkt->data)); memcpy(sp->request_sense_ptr, pkt->data, sense_sz); DEBUG5(qla2x00_dump_buffer(sp->request_sense_ptr, sense_sz)); sp->request_sense_ptr += sense_sz; sp->request_sense_length -= sense_sz; /* Place command on done queue. */ if (sp->request_sense_length == 0) { ha->status_srb = NULL; qla2x00_sp_compl(ha, sp); } } }
static void qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, void *data) { fc_port_t *fcport = data; if (fcport->ha->max_q_depth <= sdev->queue_depth) return; if (sdev->ordered_tags) scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, sdev->queue_depth + 1); else scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, sdev->queue_depth + 1); fcport->last_ramp_up = jiffies; DEBUG2(qla_printk(KERN_INFO, fcport->ha, "scsi(%ld:%d:%d:%d): Queue depth adjusted-up to %d.\n", fcport->ha->host_no, sdev->channel, sdev->id, sdev->lun, sdev->queue_depth)); }
/** * qla24xx_ms_entry() - Process a Management Server entry. * @ha: SCSI driver HA context * @index: Response queue out pointer */ static void qla24xx_ms_entry(scsi_qla_host_t *ha, struct ct_entry_24xx *pkt) { srb_t *sp; DEBUG3(printk("%s(%ld): pkt=%p pkthandle=%d.\n", __func__, ha->host_no, pkt, pkt->handle)); DEBUG9(printk("%s: ct pkt dump:\n", __func__)); DEBUG9(qla2x00_dump_buffer((void *)pkt, sizeof(struct ct_entry_24xx))); /* Validate handle. */ if (pkt->handle < MAX_OUTSTANDING_COMMANDS) sp = ha->outstanding_cmds[pkt->handle]; else sp = NULL; if (sp == NULL) { DEBUG2(printk("scsi(%ld): MS entry - invalid handle\n", ha->host_no)); DEBUG10(printk("scsi(%ld): MS entry - invalid handle\n", ha->host_no)); qla_printk(KERN_WARNING, ha, "MS entry - invalid handle %d\n", pkt->handle); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); return; } CMD_COMPL_STATUS(sp->cmd) = le16_to_cpu(pkt->comp_status); CMD_ENTRY_STATUS(sp->cmd) = pkt->entry_status; /* Free outstanding command slot. */ ha->outstanding_cmds[pkt->handle] = NULL; qla2x00_sp_compl(ha, sp); }
static int qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job) { struct Scsi_Host *host = bsg_job->shost; scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; int ret = 0; uint32_t len; uint32_t oper; bsg_job->reply->reply_payload_rcv_len = 0; if (!IS_QLA24XX_TYPE(ha) || !IS_QLA25XX(ha)) { ret = -EINVAL; goto exit_fcp_prio_cfg; } if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { ret = -EBUSY; goto exit_fcp_prio_cfg; } /* Get the sub command */ oper = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; /* Only set config is allowed if config memory is not allocated */ if (!ha->fcp_prio_cfg && (oper != QLFC_FCP_PRIO_SET_CONFIG)) { ret = -EINVAL; goto exit_fcp_prio_cfg; } switch (oper) { case QLFC_FCP_PRIO_DISABLE: if (ha->flags.fcp_prio_enabled) { ha->flags.fcp_prio_enabled = 0; ha->fcp_prio_cfg->attributes &= ~FCP_PRIO_ATTR_ENABLE; qla24xx_update_all_fcp_prio(vha); bsg_job->reply->result = DID_OK; } else { ret = -EINVAL; bsg_job->reply->result = (DID_ERROR << 16); goto exit_fcp_prio_cfg; } break; case QLFC_FCP_PRIO_ENABLE: if (!ha->flags.fcp_prio_enabled) { if (ha->fcp_prio_cfg) { ha->flags.fcp_prio_enabled = 1; ha->fcp_prio_cfg->attributes |= FCP_PRIO_ATTR_ENABLE; qla24xx_update_all_fcp_prio(vha); bsg_job->reply->result = DID_OK; } else { ret = -EINVAL; bsg_job->reply->result = (DID_ERROR << 16); goto exit_fcp_prio_cfg; } } break; case QLFC_FCP_PRIO_GET_CONFIG: len = bsg_job->reply_payload.payload_len; if (!len || len > FCP_PRIO_CFG_SIZE) { ret = -EINVAL; bsg_job->reply->result = (DID_ERROR << 16); goto exit_fcp_prio_cfg; } bsg_job->reply->result = DID_OK; bsg_job->reply->reply_payload_rcv_len = sg_copy_from_buffer( bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, ha->fcp_prio_cfg, len); break; case QLFC_FCP_PRIO_SET_CONFIG: len = bsg_job->request_payload.payload_len; if (!len || len > FCP_PRIO_CFG_SIZE) { bsg_job->reply->result = (DID_ERROR << 16); ret = -EINVAL; goto exit_fcp_prio_cfg; } if (!ha->fcp_prio_cfg) { ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE); if (!ha->fcp_prio_cfg) { qla_printk(KERN_WARNING, ha, "Unable to allocate memory " "for fcp prio config data (%x).\n", FCP_PRIO_CFG_SIZE); bsg_job->reply->result = (DID_ERROR << 16); ret = -ENOMEM; goto exit_fcp_prio_cfg; } } memset(ha->fcp_prio_cfg, 0, FCP_PRIO_CFG_SIZE); sg_copy_to_buffer(bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, ha->fcp_prio_cfg, FCP_PRIO_CFG_SIZE); /* validate fcp priority data */ if (!qla24xx_fcp_prio_cfg_valid( (struct qla_fcp_prio_cfg *) ha->fcp_prio_cfg, 1)) { bsg_job->reply->result = (DID_ERROR << 16); ret = -EINVAL; /* If buffer was invalidatic int * fcp_prio_cfg is of no use */ vfree(ha->fcp_prio_cfg); ha->fcp_prio_cfg = NULL; goto exit_fcp_prio_cfg; } ha->flags.fcp_prio_enabled = 0; if (ha->fcp_prio_cfg->attributes & FCP_PRIO_ATTR_ENABLE) ha->flags.fcp_prio_enabled = 1; qla24xx_update_all_fcp_prio(vha); bsg_job->reply->result = DID_OK; break; default: ret = -EINVAL; break; } exit_fcp_prio_cfg: bsg_job->job_done(bsg_job); return ret; }
static int qla2x00_process_els(struct fc_bsg_job *bsg_job) { struct fc_rport *rport; fc_port_t *fcport = NULL; struct Scsi_Host *host; scsi_qla_host_t *vha; struct qla_hw_data *ha; srb_t *sp; const char *type; int req_sg_cnt, rsp_sg_cnt; int rval = (DRIVER_ERROR << 16); uint16_t nextlid = 0; struct srb_ctx *els; if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) { rport = bsg_job->rport; fcport = *(fc_port_t **) rport->dd_data; host = rport_to_shost(rport); vha = shost_priv(host); ha = vha->hw; type = "FC_BSG_RPT_ELS"; } else { host = bsg_job->shost; vha = shost_priv(host); ha = vha->hw; type = "FC_BSG_HST_ELS_NOLOGIN"; } /* pass through is supported only for ISP 4Gb or higher */ if (!IS_FWI2_CAPABLE(ha)) { DEBUG2(qla_printk(KERN_INFO, ha, "scsi(%ld):ELS passthru not supported for ISP23xx based " "adapters\n", vha->host_no)); rval = -EPERM; goto done; } /* Multiple SG's are not supported for ELS requests */ if (bsg_job->request_payload.sg_cnt > 1 || bsg_job->reply_payload.sg_cnt > 1) { DEBUG2(printk(KERN_INFO "multiple SG's are not supported for ELS requests" " [request_sg_cnt: %x reply_sg_cnt: %x]\n", bsg_job->request_payload.sg_cnt, bsg_job->reply_payload.sg_cnt)); rval = -EPERM; goto done; } /* ELS request for rport */ if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) { /* make sure the rport is logged in, * if not perform fabric login */ if (qla2x00_fabric_login(vha, fcport, &nextlid)) { DEBUG2(qla_printk(KERN_WARNING, ha, "failed to login port %06X for ELS passthru\n", fcport->d_id.b24)); rval = -EIO; goto done; } } else { /* Allocate a dummy fcport structure, since functions * preparing the IOCB and mailbox command retrieves port * specific information from fcport structure. For Host based * ELS commands there will be no fcport structure allocated */ fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); if (!fcport) { rval = -ENOMEM; goto done; } /* Initialize all required fields of fcport */ fcport->vha = vha; fcport->vp_idx = vha->vp_idx; fcport->d_id.b.al_pa = bsg_job->request->rqst_data.h_els.port_id[0]; fcport->d_id.b.area = bsg_job->request->rqst_data.h_els.port_id[1]; fcport->d_id.b.domain = bsg_job->request->rqst_data.h_els.port_id[2]; fcport->loop_id = (fcport->d_id.b.al_pa == 0xFD) ? NPH_FABRIC_CONTROLLER : NPH_F_PORT; } if (!vha->flags.online) { DEBUG2(qla_printk(KERN_WARNING, ha, "host not online\n")); rval = -EIO; goto done; } req_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); if (!req_sg_cnt) { rval = -ENOMEM; goto done_free_fcport; } rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); if (!rsp_sg_cnt) { rval = -ENOMEM; goto done_free_fcport; } if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { DEBUG2(printk(KERN_INFO "dma mapping resulted in different sg counts \ [request_sg_cnt: %x dma_request_sg_cnt: %x\ reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n", bsg_job->request_payload.sg_cnt, req_sg_cnt, bsg_job->reply_payload.sg_cnt, rsp_sg_cnt)); rval = -EAGAIN; goto done_unmap_sg; }
void qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) { int rval; uint32_t cnt; uint32_t risc_address; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; uint32_t __iomem *dmp_reg; uint32_t *iter_reg; uint16_t __iomem *mbx_reg; unsigned long flags; struct qla24xx_fw_dump *fw; uint32_t ext_mem_cnt; void *nxt; risc_address = ext_mem_cnt = 0; flags = 0; if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (!ha->fw_dump) { qla_printk(KERN_WARNING, ha, "No buffer available for dump!!!\n"); goto qla24xx_fw_dump_failed; } if (ha->fw_dumped) { qla_printk(KERN_WARNING, ha, "Firmware has been previously dumped (%p) -- ignoring " "request...\n", ha->fw_dump); goto qla24xx_fw_dump_failed; } fw = &ha->fw_dump->isp.isp24; qla2xxx_prep_dump(ha, ha->fw_dump); fw->host_status = htonl(RD_REG_DWORD(®->host_status)); /* Pause RISC. */ rval = qla24xx_pause_risc(reg); if (rval != QLA_SUCCESS) goto qla24xx_fw_dump_failed_0; /* Host interface registers. */ dmp_reg = ®->flash_addr; for (cnt = 0; cnt < sizeof(fw->host_reg) / 4; cnt++) fw->host_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); /* Disable interrupts. */ WRT_REG_DWORD(®->ictrl, 0); RD_REG_DWORD(®->ictrl); /* Shadow registers. */ WRT_REG_DWORD(®->iobase_addr, 0x0F70); RD_REG_DWORD(®->iobase_addr); WRT_REG_DWORD(®->iobase_select, 0xB0000000); fw->shadow_reg[0] = htonl(RD_REG_DWORD(®->iobase_sdata)); WRT_REG_DWORD(®->iobase_select, 0xB0100000); fw->shadow_reg[1] = htonl(RD_REG_DWORD(®->iobase_sdata)); WRT_REG_DWORD(®->iobase_select, 0xB0200000); fw->shadow_reg[2] = htonl(RD_REG_DWORD(®->iobase_sdata)); WRT_REG_DWORD(®->iobase_select, 0xB0300000); fw->shadow_reg[3] = htonl(RD_REG_DWORD(®->iobase_sdata)); WRT_REG_DWORD(®->iobase_select, 0xB0400000); fw->shadow_reg[4] = htonl(RD_REG_DWORD(®->iobase_sdata)); WRT_REG_DWORD(®->iobase_select, 0xB0500000); fw->shadow_reg[5] = htonl(RD_REG_DWORD(®->iobase_sdata)); WRT_REG_DWORD(®->iobase_select, 0xB0600000); fw->shadow_reg[6] = htonl(RD_REG_DWORD(®->iobase_sdata)); /* Mailbox registers. */ mbx_reg = ®->mailbox0; for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++) fw->mailbox_reg[cnt] = htons(RD_REG_WORD(mbx_reg++)); /* Transfer sequence registers. */ iter_reg = fw->xseq_gp_reg; iter_reg = qla24xx_read_window(reg, 0xBF00, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0xBF10, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0xBF20, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0xBF30, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0xBF40, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0xBF50, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0xBF60, 16, iter_reg); qla24xx_read_window(reg, 0xBF70, 16, iter_reg); qla24xx_read_window(reg, 0xBFE0, 16, fw->xseq_0_reg); qla24xx_read_window(reg, 0xBFF0, 16, fw->xseq_1_reg); /* Receive sequence registers. */ iter_reg = fw->rseq_gp_reg; iter_reg = qla24xx_read_window(reg, 0xFF00, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0xFF10, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0xFF20, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0xFF30, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0xFF40, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0xFF50, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0xFF60, 16, iter_reg); qla24xx_read_window(reg, 0xFF70, 16, iter_reg); qla24xx_read_window(reg, 0xFFD0, 16, fw->rseq_0_reg); qla24xx_read_window(reg, 0xFFE0, 16, fw->rseq_1_reg); qla24xx_read_window(reg, 0xFFF0, 16, fw->rseq_2_reg); /* Command DMA registers. */ qla24xx_read_window(reg, 0x7100, 16, fw->cmd_dma_reg); /* Queues. */ iter_reg = fw->req0_dma_reg; iter_reg = qla24xx_read_window(reg, 0x7200, 8, iter_reg); dmp_reg = ®->iobase_q; for (cnt = 0; cnt < 7; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); iter_reg = fw->resp0_dma_reg; iter_reg = qla24xx_read_window(reg, 0x7300, 8, iter_reg); dmp_reg = ®->iobase_q; for (cnt = 0; cnt < 7; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); iter_reg = fw->req1_dma_reg; iter_reg = qla24xx_read_window(reg, 0x7400, 8, iter_reg); dmp_reg = ®->iobase_q; for (cnt = 0; cnt < 7; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); /* Transmit DMA registers. */ iter_reg = fw->xmt0_dma_reg; iter_reg = qla24xx_read_window(reg, 0x7600, 16, iter_reg); qla24xx_read_window(reg, 0x7610, 16, iter_reg); iter_reg = fw->xmt1_dma_reg; iter_reg = qla24xx_read_window(reg, 0x7620, 16, iter_reg); qla24xx_read_window(reg, 0x7630, 16, iter_reg); iter_reg = fw->xmt2_dma_reg; iter_reg = qla24xx_read_window(reg, 0x7640, 16, iter_reg); qla24xx_read_window(reg, 0x7650, 16, iter_reg); iter_reg = fw->xmt3_dma_reg; iter_reg = qla24xx_read_window(reg, 0x7660, 16, iter_reg); qla24xx_read_window(reg, 0x7670, 16, iter_reg); iter_reg = fw->xmt4_dma_reg; iter_reg = qla24xx_read_window(reg, 0x7680, 16, iter_reg); qla24xx_read_window(reg, 0x7690, 16, iter_reg); qla24xx_read_window(reg, 0x76A0, 16, fw->xmt_data_dma_reg); /* Receive DMA registers. */ iter_reg = fw->rcvt0_data_dma_reg; iter_reg = qla24xx_read_window(reg, 0x7700, 16, iter_reg); qla24xx_read_window(reg, 0x7710, 16, iter_reg); iter_reg = fw->rcvt1_data_dma_reg; iter_reg = qla24xx_read_window(reg, 0x7720, 16, iter_reg); qla24xx_read_window(reg, 0x7730, 16, iter_reg); /* RISC registers. */ iter_reg = fw->risc_gp_reg; iter_reg = qla24xx_read_window(reg, 0x0F00, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x0F10, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x0F20, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x0F30, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x0F40, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x0F50, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x0F60, 16, iter_reg); qla24xx_read_window(reg, 0x0F70, 16, iter_reg); /* Local memory controller registers. */ iter_reg = fw->lmc_reg; iter_reg = qla24xx_read_window(reg, 0x3000, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x3010, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x3020, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x3030, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x3040, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x3050, 16, iter_reg); qla24xx_read_window(reg, 0x3060, 16, iter_reg); /* Fibre Protocol Module registers. */ iter_reg = fw->fpm_hdw_reg; iter_reg = qla24xx_read_window(reg, 0x4000, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x4010, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x4020, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x4030, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x4040, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x4050, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x4060, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x4070, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x4080, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x4090, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x40A0, 16, iter_reg); qla24xx_read_window(reg, 0x40B0, 16, iter_reg); /* Frame Buffer registers. */ iter_reg = fw->fb_hdw_reg; iter_reg = qla24xx_read_window(reg, 0x6000, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x6010, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x6020, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x6030, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x6040, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x6100, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x6130, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x6150, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x6170, 16, iter_reg); iter_reg = qla24xx_read_window(reg, 0x6190, 16, iter_reg); qla24xx_read_window(reg, 0x61B0, 16, iter_reg); rval = qla24xx_soft_reset(ha); if (rval != QLA_SUCCESS) goto qla24xx_fw_dump_failed_0; rval = qla24xx_dump_memory(ha, fw->code_ram, sizeof(fw->code_ram), fw->ext_mem, &nxt); if (rval != QLA_SUCCESS) goto qla24xx_fw_dump_failed_0; nxt = qla2xxx_copy_queues(ha, nxt); if (ha->eft) memcpy(nxt, ha->eft, ntohl(ha->fw_dump->eft_size)); qla24xx_fw_dump_failed_0: if (rval != QLA_SUCCESS) { qla_printk(KERN_WARNING, ha, "Failed to dump firmware (%x)!!!\n", rval); ha->fw_dumped = 0; } else { qla_printk(KERN_INFO, ha, "Firmware dump saved to temp buffer (%ld/%p).\n", ha->host_no, ha->fw_dump); ha->fw_dumped = 1; } qla24xx_fw_dump_failed: if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); }
/** * qla2300_fw_dump() - Dumps binary data from the 2300 firmware. * @ha: HA context * @hardware_locked: Called with the hardware_lock */ void qla2300_fw_dump(scsi_qla_host_t *ha, int hardware_locked) { int rval; uint32_t cnt, timer; uint32_t risc_address; uint16_t mb0, mb2; uint32_t stat; device_reg_t __iomem *reg = ha->iobase; uint16_t __iomem *dmp_reg; unsigned long flags; struct qla2300_fw_dump *fw; uint32_t dump_size, data_ram_cnt; risc_address = data_ram_cnt = 0; mb0 = mb2 = 0; flags = 0; if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (ha->fw_dump != NULL) { qla_printk(KERN_WARNING, ha, "Firmware has been previously dumped (%p) -- ignoring " "request...\n", ha->fw_dump); goto qla2300_fw_dump_failed; } /* Allocate (large) dump buffer. */ dump_size = sizeof(struct qla2300_fw_dump); dump_size += (ha->fw_memory_size - 0x11000) * sizeof(uint16_t); ha->fw_dump_order = get_order(dump_size); ha->fw_dump = (struct qla2300_fw_dump *) __get_free_pages(GFP_ATOMIC, ha->fw_dump_order); if (ha->fw_dump == NULL) { qla_printk(KERN_WARNING, ha, "Unable to allocated memory for firmware dump (%d/%d).\n", ha->fw_dump_order, dump_size); goto qla2300_fw_dump_failed; } fw = ha->fw_dump; rval = QLA_SUCCESS; fw->hccr = RD_REG_WORD(®->hccr); /* Pause RISC. */ WRT_REG_WORD(®->hccr, HCCR_PAUSE_RISC); if (IS_QLA2300(ha)) { for (cnt = 30000; (RD_REG_WORD(®->hccr) & HCCR_RISC_PAUSE) == 0 && rval == QLA_SUCCESS; cnt--) { if (cnt) udelay(100); else rval = QLA_FUNCTION_TIMEOUT; } } else { RD_REG_WORD(®->hccr); /* PCI Posting. */ udelay(10); } if (rval == QLA_SUCCESS) { dmp_reg = (uint16_t __iomem *)(reg + 0); for (cnt = 0; cnt < sizeof(fw->pbiu_reg) / 2; cnt++) fw->pbiu_reg[cnt] = RD_REG_WORD(dmp_reg++); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x10); for (cnt = 0; cnt < sizeof(fw->risc_host_reg) / 2; cnt++) fw->risc_host_reg[cnt] = RD_REG_WORD(dmp_reg++); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x40); for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++) fw->mailbox_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->ctrl_status, 0x40); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->resp_dma_reg) / 2; cnt++) fw->resp_dma_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->ctrl_status, 0x50); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->dma_reg) / 2; cnt++) fw->dma_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->ctrl_status, 0x00); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0xA0); for (cnt = 0; cnt < sizeof(fw->risc_hdw_reg) / 2; cnt++) fw->risc_hdw_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2000); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp0_reg) / 2; cnt++) fw->risc_gp0_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2200); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp1_reg) / 2; cnt++) fw->risc_gp1_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2400); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp2_reg) / 2; cnt++) fw->risc_gp2_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2600); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp3_reg) / 2; cnt++) fw->risc_gp3_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2800); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp4_reg) / 2; cnt++) fw->risc_gp4_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2A00); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp5_reg) / 2; cnt++) fw->risc_gp5_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2C00); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp6_reg) / 2; cnt++) fw->risc_gp6_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2E00); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp7_reg) / 2; cnt++) fw->risc_gp7_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->ctrl_status, 0x10); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->frame_buf_hdw_reg) / 2; cnt++) fw->frame_buf_hdw_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->ctrl_status, 0x20); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->fpm_b0_reg) / 2; cnt++) fw->fpm_b0_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->ctrl_status, 0x30); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->fpm_b1_reg) / 2; cnt++) fw->fpm_b1_reg[cnt] = RD_REG_WORD(dmp_reg++); /* Reset RISC. */ WRT_REG_WORD(®->ctrl_status, CSR_ISP_SOFT_RESET); for (cnt = 0; cnt < 30000; cnt++) { if ((RD_REG_WORD(®->ctrl_status) & CSR_ISP_SOFT_RESET) == 0) break; udelay(10); } } if (!IS_QLA2300(ha)) { for (cnt = 30000; RD_MAILBOX_REG(ha, reg, 0) != 0 && rval == QLA_SUCCESS; cnt--) { if (cnt) udelay(100); else rval = QLA_FUNCTION_TIMEOUT; } } if (rval == QLA_SUCCESS) { /* Get RISC SRAM. */ risc_address = 0x800; WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_WORD); clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); } for (cnt = 0; cnt < sizeof(fw->risc_ram) / 2 && rval == QLA_SUCCESS; cnt++, risc_address++) { WRT_MAILBOX_REG(ha, reg, 1, (uint16_t)risc_address); WRT_REG_WORD(®->hccr, HCCR_SET_HOST_INT); for (timer = 6000000; timer; timer--) { /* Check for pending interrupts. */ stat = RD_REG_DWORD(®->u.isp2300.host_status); if (stat & HSR_RISC_INT) { stat &= 0xff; if (stat == 0x1 || stat == 0x2) { set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); mb0 = RD_MAILBOX_REG(ha, reg, 0); mb2 = RD_MAILBOX_REG(ha, reg, 2); /* Release mailbox registers. */ WRT_REG_WORD(®->semaphore, 0); WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); break; } else if (stat == 0x10 || stat == 0x11) { set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); mb0 = RD_MAILBOX_REG(ha, reg, 0); mb2 = RD_MAILBOX_REG(ha, reg, 2); WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); break; } /* clear this intr; it wasn't a mailbox intr */ WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); } udelay(5); } if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) { rval = mb0 & MBS_MASK; fw->risc_ram[cnt] = mb2; } else { rval = QLA_FUNCTION_FAILED; } } if (rval == QLA_SUCCESS) { /* Get stack SRAM. */ risc_address = 0x10000; WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_EXTENDED); clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); } for (cnt = 0; cnt < sizeof(fw->stack_ram) / 2 && rval == QLA_SUCCESS; cnt++, risc_address++) { WRT_MAILBOX_REG(ha, reg, 1, LSW(risc_address)); WRT_MAILBOX_REG(ha, reg, 8, MSW(risc_address)); WRT_REG_WORD(®->hccr, HCCR_SET_HOST_INT); for (timer = 6000000; timer; timer--) { /* Check for pending interrupts. */ stat = RD_REG_DWORD(®->u.isp2300.host_status); if (stat & HSR_RISC_INT) { stat &= 0xff; if (stat == 0x1 || stat == 0x2) { set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); mb0 = RD_MAILBOX_REG(ha, reg, 0); mb2 = RD_MAILBOX_REG(ha, reg, 2); /* Release mailbox registers. */ WRT_REG_WORD(®->semaphore, 0); WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); break; } else if (stat == 0x10 || stat == 0x11) { set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); mb0 = RD_MAILBOX_REG(ha, reg, 0); mb2 = RD_MAILBOX_REG(ha, reg, 2); WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); break; } /* clear this intr; it wasn't a mailbox intr */ WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); } udelay(5); } if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) { rval = mb0 & MBS_MASK; fw->stack_ram[cnt] = mb2; } else { rval = QLA_FUNCTION_FAILED; } } if (rval == QLA_SUCCESS) { /* Get data SRAM. */ risc_address = 0x11000; data_ram_cnt = ha->fw_memory_size - risc_address + 1; WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_EXTENDED); clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); } for (cnt = 0; cnt < data_ram_cnt && rval == QLA_SUCCESS; cnt++, risc_address++) { WRT_MAILBOX_REG(ha, reg, 1, LSW(risc_address)); WRT_MAILBOX_REG(ha, reg, 8, MSW(risc_address)); WRT_REG_WORD(®->hccr, HCCR_SET_HOST_INT); for (timer = 6000000; timer; timer--) { /* Check for pending interrupts. */ stat = RD_REG_DWORD(®->u.isp2300.host_status); if (stat & HSR_RISC_INT) { stat &= 0xff; if (stat == 0x1 || stat == 0x2) { set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); mb0 = RD_MAILBOX_REG(ha, reg, 0); mb2 = RD_MAILBOX_REG(ha, reg, 2); /* Release mailbox registers. */ WRT_REG_WORD(®->semaphore, 0); WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); break; } else if (stat == 0x10 || stat == 0x11) { set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); mb0 = RD_MAILBOX_REG(ha, reg, 0); mb2 = RD_MAILBOX_REG(ha, reg, 2); WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); break; } /* clear this intr; it wasn't a mailbox intr */ WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); } udelay(5); } if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) { rval = mb0 & MBS_MASK; fw->data_ram[cnt] = mb2; } else { rval = QLA_FUNCTION_FAILED; } } if (rval != QLA_SUCCESS) { qla_printk(KERN_WARNING, ha, "Failed to dump firmware (%x)!!!\n", rval); free_pages((unsigned long)ha->fw_dump, ha->fw_dump_order); ha->fw_dump = NULL; } else { qla_printk(KERN_INFO, ha, "Firmware dump saved to temp buffer (%ld/%p).\n", ha->host_no, ha->fw_dump); } qla2300_fw_dump_failed: if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); }
/** * qla24xx_intr_handler() - Process interrupts for the ISP23xx and ISP63xx. * @irq: * @dev_id: SCSI driver HA context * @regs: * * Called by system whenever the host adapter generates an interrupt. * * Returns handled flag. */ irqreturn_t qla24xx_intr_handler(int irq, void *dev_id, struct pt_regs *regs) { scsi_qla_host_t *ha; struct device_reg_24xx __iomem *reg; int status; unsigned long flags; unsigned long iter; uint32_t stat; uint32_t hccr; uint16_t mb[4]; ha = (scsi_qla_host_t *) dev_id; if (!ha) { printk(KERN_INFO "%s(): NULL host pointer\n", __func__); return IRQ_NONE; } reg = &ha->iobase->isp24; status = 0; spin_lock_irqsave(&ha->hardware_lock, flags); for (iter = 50; iter--; ) { stat = RD_REG_DWORD(®->host_status); if (stat & HSRX_RISC_PAUSED) { hccr = RD_REG_DWORD(®->hccr); qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " "Dumping firmware!\n", hccr); qla24xx_fw_dump(ha, 1); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; } else if ((stat & HSRX_RISC_INT) == 0) break; switch (stat & 0xff) { case 0x1: case 0x2: case 0x10: case 0x11: qla24xx_mbx_completion(ha, MSW(stat)); status |= MBX_INTERRUPT; break; case 0x12: mb[0] = MSW(stat); mb[1] = RD_REG_WORD(®->mailbox1); mb[2] = RD_REG_WORD(®->mailbox2); mb[3] = RD_REG_WORD(®->mailbox3); qla2x00_async_event(ha, mb); break; case 0x13: qla24xx_process_response_queue(ha); break; default: DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " "(%d).\n", ha->host_no, stat & 0xff)); break; } WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); RD_REG_DWORD_RELAXED(®->hccr); } spin_unlock_irqrestore(&ha->hardware_lock, flags); if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && (status & MBX_INTERRUPT) && ha->flags.mbox_int) { spin_lock_irqsave(&ha->mbx_reg_lock, flags); set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); up(&ha->mbx_intr_sem); spin_unlock_irqrestore(&ha->mbx_reg_lock, flags); } return IRQ_HANDLED; }
/** * qla2300_intr_handler() - Process interrupts for the ISP23xx and ISP63xx. * @irq: * @dev_id: SCSI driver HA context * @regs: * * Called by system whenever the host adapter generates an interrupt. * * Returns handled flag. */ irqreturn_t qla2300_intr_handler(int irq, void *dev_id, struct pt_regs *regs) { scsi_qla_host_t *ha; struct device_reg_2xxx __iomem *reg; int status; unsigned long flags; unsigned long iter; uint32_t stat; uint16_t hccr; uint16_t mb[4]; ha = (scsi_qla_host_t *) dev_id; if (!ha) { printk(KERN_INFO "%s(): NULL host pointer\n", __func__); return (IRQ_NONE); } reg = &ha->iobase->isp; status = 0; spin_lock_irqsave(&ha->hardware_lock, flags); for (iter = 50; iter--; ) { stat = RD_REG_DWORD(®->u.isp2300.host_status); if (stat & HSR_RISC_PAUSED) { hccr = RD_REG_WORD(®->hccr); if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8)) qla_printk(KERN_INFO, ha, "Parity error -- HCCR=%x.\n", hccr); else qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x.\n", hccr); /* * Issue a "HARD" reset in order for the RISC * interrupt bit to be cleared. Schedule a big * hammmer to get out of the RISC PAUSED state. */ WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); RD_REG_WORD(®->hccr); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; } else if ((stat & HSR_RISC_INT) == 0) break; switch (stat & 0xff) { case 0x1: case 0x2: case 0x10: case 0x11: qla2x00_mbx_completion(ha, MSW(stat)); status |= MBX_INTERRUPT; /* Release mailbox registers. */ WRT_REG_WORD(®->semaphore, 0); break; case 0x12: mb[0] = MSW(stat); mb[1] = RD_MAILBOX_REG(ha, reg, 1); mb[2] = RD_MAILBOX_REG(ha, reg, 2); mb[3] = RD_MAILBOX_REG(ha, reg, 3); qla2x00_async_event(ha, mb); break; case 0x13: qla2x00_process_response_queue(ha); break; case 0x15: mb[0] = MBA_CMPLT_1_16BIT; mb[1] = MSW(stat); qla2x00_async_event(ha, mb); break; case 0x16: mb[0] = MBA_SCSI_COMPLETION; mb[1] = MSW(stat); mb[2] = RD_MAILBOX_REG(ha, reg, 2); qla2x00_async_event(ha, mb); break; default: DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " "(%d).\n", ha->host_no, stat & 0xff)); break; } WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD_RELAXED(®->hccr); } spin_unlock_irqrestore(&ha->hardware_lock, flags); if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && (status & MBX_INTERRUPT) && ha->flags.mbox_int) { spin_lock_irqsave(&ha->mbx_reg_lock, flags); set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); up(&ha->mbx_intr_sem); spin_unlock_irqrestore(&ha->mbx_reg_lock, flags); } return (IRQ_HANDLED); }
/** * qla2x00_async_event() - Process aynchronous events. * @ha: SCSI driver HA context * @mb0: Mailbox0 register */ static void qla2x00_async_event(scsi_qla_host_t *ha, uint32_t mbx) { static char *link_speeds[5] = { "1", "2", "4", "?", "10" }; char *link_speed; uint16_t mb[4]; uint16_t handle_cnt; uint16_t cnt; uint32_t handles[5]; device_reg_t *reg = ha->iobase; uint32_t rscn_entry, host_pid; uint8_t rscn_queue_index; /* Setup to process RIO completion. */ handle_cnt = 0; mb[0] = LSW(mbx); switch (mb[0]) { case MBA_SCSI_COMPLETION: if (IS_QLA2100(ha) || IS_QLA2200(ha)) handles[0] = le32_to_cpu( ((uint32_t)(RD_MAILBOX_REG(ha, reg, 2) << 16)) | RD_MAILBOX_REG(ha, reg, 1)); else handles[0] = le32_to_cpu( ((uint32_t)(RD_MAILBOX_REG(ha, reg, 2) << 16)) | MSW(mbx)); handle_cnt = 1; break; case MBA_CMPLT_1_16BIT: if (IS_QLA2100(ha) || IS_QLA2200(ha)) handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1); else handles[0] = MSW(mbx); handle_cnt = 1; mb[0] = MBA_SCSI_COMPLETION; break; case MBA_CMPLT_2_16BIT: handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1); handles[1] = (uint32_t)RD_MAILBOX_REG(ha, reg, 2); handle_cnt = 2; mb[0] = MBA_SCSI_COMPLETION; break; case MBA_CMPLT_3_16BIT: handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1); handles[1] = (uint32_t)RD_MAILBOX_REG(ha, reg, 2); handles[2] = (uint32_t)RD_MAILBOX_REG(ha, reg, 3); handle_cnt = 3; mb[0] = MBA_SCSI_COMPLETION; break; case MBA_CMPLT_4_16BIT: handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1); handles[1] = (uint32_t)RD_MAILBOX_REG(ha, reg, 2); handles[2] = (uint32_t)RD_MAILBOX_REG(ha, reg, 3); handles[3] = (uint32_t)RD_MAILBOX_REG(ha, reg, 6); handle_cnt = 4; mb[0] = MBA_SCSI_COMPLETION; break; case MBA_CMPLT_5_16BIT: handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1); handles[1] = (uint32_t)RD_MAILBOX_REG(ha, reg, 2); handles[2] = (uint32_t)RD_MAILBOX_REG(ha, reg, 3); handles[3] = (uint32_t)RD_MAILBOX_REG(ha, reg, 6); handles[4] = (uint32_t)RD_MAILBOX_REG(ha, reg, 7); handle_cnt = 5; mb[0] = MBA_SCSI_COMPLETION; break; case MBA_CMPLT_2_32BIT: handles[0] = le32_to_cpu( ((uint32_t)(RD_MAILBOX_REG(ha, reg, 2) << 16)) | RD_MAILBOX_REG(ha, reg, 1)); handles[1] = le32_to_cpu( ((uint32_t)(RD_MAILBOX_REG(ha, reg, 7) << 16)) | RD_MAILBOX_REG(ha, reg, 6)); handle_cnt = 2; mb[0] = MBA_SCSI_COMPLETION; break; default: break; } mb[0] = LSW(mbx); switch (mb[0]) { case MBA_SCSI_COMPLETION: /* Fast Post */ if (!ha->flags.online) break; for (cnt = 0; cnt < handle_cnt; cnt++) qla2x00_process_completed_request(ha, handles[cnt]); break; case MBA_RESET: /* Reset */ DEBUG2(printk("scsi(%ld): Asynchronous RESET.\n", ha->host_no)); set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); break; case MBA_SYSTEM_ERR: /* System Error */ mb[1] = RD_MAILBOX_REG(ha, reg, 1); mb[2] = RD_MAILBOX_REG(ha, reg, 2); mb[3] = RD_MAILBOX_REG(ha, reg, 3); qla_printk(KERN_INFO, ha, "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n", mb[1], mb[2], mb[3]); if (IS_QLA2100(ha) || IS_QLA2200(ha)) qla2100_fw_dump(ha, 1); else qla2300_fw_dump(ha, 1); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */ DEBUG2(printk("scsi(%ld): ISP Request Transfer Error.\n", ha->host_no)); qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n"); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */ DEBUG2(printk("scsi(%ld): ISP Response Transfer Error.\n", ha->host_no)); qla_printk(KERN_WARNING, ha, "ISP Response Transfer Error.\n"); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; case MBA_WAKEUP_THRES: /* Request Queue Wake-up */ DEBUG2(printk("scsi(%ld): Asynchronous WAKEUP_THRES.\n", ha->host_no)); break; case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */ mb[1] = RD_MAILBOX_REG(ha, reg, 1); DEBUG2(printk("scsi(%ld): LIP occured (%x).\n", ha->host_no, mb[1])); qla_printk(KERN_INFO, ha, "LIP occured (%x).\n", mb[1]); if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); qla2x00_mark_all_devices_lost(ha); } set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags); ha->flags.management_server_logged_in = 0; /* Update AEN queue. */ qla2x00_enqueue_aen(ha, MBA_LIP_OCCURRED, NULL); ha->total_lip_cnt++; break; case MBA_LOOP_UP: /* Loop Up Event */ mb[1] = RD_MAILBOX_REG(ha, reg, 1); ha->link_data_rate = 0; if (IS_QLA2100(ha) || IS_QLA2200(ha)) { link_speed = link_speeds[0]; } else { link_speed = link_speeds[3]; if (mb[1] < 5) link_speed = link_speeds[mb[1]]; ha->link_data_rate = mb[1]; } DEBUG2(printk("scsi(%ld): Asynchronous LOOP UP (%s Gbps).\n", ha->host_no, link_speed)); qla_printk(KERN_INFO, ha, "LOOP UP detected (%s Gbps).\n", link_speed); ha->flags.management_server_logged_in = 0; /* Update AEN queue. */ qla2x00_enqueue_aen(ha, MBA_LOOP_UP, NULL); break; case MBA_LOOP_DOWN: /* Loop Down Event */ DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN.\n", ha->host_no)); qla_printk(KERN_INFO, ha, "LOOP DOWN detected.\n"); if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); ha->device_flags |= DFLG_NO_CABLE; qla2x00_mark_all_devices_lost(ha); } ha->flags.management_server_logged_in = 0; ha->link_data_rate = 0; /* Update AEN queue. */ qla2x00_enqueue_aen(ha, MBA_LOOP_DOWN, NULL); break; case MBA_LIP_RESET: /* LIP reset occurred */ mb[1] = RD_MAILBOX_REG(ha, reg, 1); DEBUG2(printk("scsi(%ld): Asynchronous LIP RESET (%x).\n", ha->host_no, mb[1])); qla_printk(KERN_INFO, ha, "LIP reset occured (%x).\n", mb[1]); if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); qla2x00_mark_all_devices_lost(ha); } set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); ha->operating_mode = LOOP; ha->flags.management_server_logged_in = 0; /* Update AEN queue. */ qla2x00_enqueue_aen(ha, MBA_LIP_RESET, NULL); ha->total_lip_cnt++; break; case MBA_POINT_TO_POINT: /* Point-to-Point */ if (IS_QLA2100(ha)) break; DEBUG2(printk("scsi(%ld): Asynchronous P2P MODE received.\n", ha->host_no)); /* * Until there's a transition from loop down to loop up, treat * this as loop down only. */ if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); if (!atomic_read(&ha->loop_down_timer)) atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); qla2x00_mark_all_devices_lost(ha); } if (!(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) { set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); } set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags); break; case MBA_CHG_IN_CONNECTION: /* Change in connection mode */ if (IS_QLA2100(ha)) break; mb[1] = RD_MAILBOX_REG(ha, reg, 1); DEBUG2(printk("scsi(%ld): Asynchronous Change In Connection " "received.\n", ha->host_no)); qla_printk(KERN_INFO, ha, "Configuration change detected: value=%x.\n", mb[1]); if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); if (!atomic_read(&ha->loop_down_timer)) atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); qla2x00_mark_all_devices_lost(ha); } set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); break; case MBA_PORT_UPDATE: /* Port database update */ mb[1] = RD_MAILBOX_REG(ha, reg, 1); mb[2] = RD_MAILBOX_REG(ha, reg, 2); /* * If a single remote port just logged into (or logged out of) * us, create a new entry in our rscn fcports list and handle * the event like an RSCN. */ if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA6312(ha) && !IS_QLA6322(ha) && ha->flags.init_done && mb[1] != 0xffff && ((ha->operating_mode == P2P && mb[1] != 0) || (ha->operating_mode != P2P && mb[1] != SNS_FIRST_LOOP_ID)) && (mb[2] == 6 || mb[2] == 7)) { int rval; fc_port_t *rscn_fcport; /* Create new fcport for login. */ rscn_fcport = qla2x00_alloc_rscn_fcport(ha, GFP_ATOMIC); if (rscn_fcport) { DEBUG14(printk("scsi(%ld): Port Update -- " "creating RSCN fcport %p for login.\n", ha->host_no, rscn_fcport)); rscn_fcport->loop_id = mb[1]; rscn_fcport->d_id.b24 = INVALID_PORT_ID; atomic_set(&rscn_fcport->state, FCS_DEVICE_LOST); list_add_tail(&rscn_fcport->list, &ha->rscn_fcports); rval = qla2x00_handle_port_rscn(ha, 0, rscn_fcport, 1); if (rval == QLA_SUCCESS) break; } else { DEBUG14(printk("scsi(%ld): Port Update -- " "-- unable to allocate RSCN fcport " "login.\n", ha->host_no)); } } /* * If PORT UPDATE is global (recieved LIP_OCCURED/LIP_RESET * event etc. earlier indicating loop is down) then process * it. Otherwise ignore it and Wait for RSCN to come in. */ if (atomic_read(&ha->loop_state) != LOOP_DOWN) { DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE " "ignored.\n", ha->host_no)); break; } DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE.\n", ha->host_no)); DEBUG(printk(KERN_INFO "scsi(%ld): Port database changed %04x %04x.\n", ha->host_no, mb[1], mb[2])); /* * Mark all devices as missing so we will login again. */ atomic_set(&ha->loop_state, LOOP_UP); atomic_set(&ha->loop_down_timer, 0); qla2x00_mark_all_devices_lost(ha); ha->flags.rscn_queue_overflow = 1; set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); /* Update AEN queue. */ qla2x00_enqueue_aen(ha, MBA_PORT_UPDATE, NULL); break; case MBA_RSCN_UPDATE: /* State Change Registration */ mb[1] = RD_MAILBOX_REG(ha, reg, 1); mb[2] = RD_MAILBOX_REG(ha, reg, 2); DEBUG2(printk("scsi(%ld): Asynchronous RSCR UPDATE.\n", ha->host_no)); DEBUG(printk(KERN_INFO "scsi(%ld): RSCN database changed -- %04x %04x.\n", ha->host_no, mb[1], mb[2])); rscn_entry = (mb[1] << 16) | mb[2]; host_pid = (ha->d_id.b.domain << 16) | (ha->d_id.b.area << 8) | ha->d_id.b.al_pa; if (rscn_entry == host_pid) { DEBUG(printk(KERN_INFO "scsi(%ld): Ignoring RSCN update to local host " "port ID (%06x)\n", ha->host_no, host_pid)); break; } rscn_queue_index = ha->rscn_in_ptr + 1; if (rscn_queue_index == MAX_RSCN_COUNT) rscn_queue_index = 0; if (rscn_queue_index != ha->rscn_out_ptr) { ha->rscn_queue[ha->rscn_in_ptr] = rscn_entry; ha->rscn_in_ptr = rscn_queue_index; } else { ha->flags.rscn_queue_overflow = 1; } atomic_set(&ha->loop_state, LOOP_UPDATE); atomic_set(&ha->loop_down_timer, 0); ha->flags.management_server_logged_in = 0; set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); set_bit(RSCN_UPDATE, &ha->dpc_flags); /* Update AEN queue. */ qla2x00_enqueue_aen(ha, MBA_RSCN_UPDATE, &mb[0]); break; /* case MBA_RIO_RESPONSE: */ case MBA_ZIO_RESPONSE: DEBUG2(printk("scsi(%ld): [R|Z]IO update completion.\n", ha->host_no)); DEBUG(printk(KERN_INFO "scsi(%ld): [R|Z]IO update completion.\n", ha->host_no)); qla2x00_process_response_queue(ha); break; } }
/** * qla2x00_intr_handler() - Process interrupts for the ISP. * @irq: * @dev_id: SCSI driver HA context * @regs: * * Called by system whenever the host adapter generates an interrupt. * * Returns handled flag. */ irqreturn_t qla2x00_intr_handler(int irq, void *dev_id, struct pt_regs *regs) { scsi_qla_host_t *ha; device_reg_t *reg; uint32_t mbx; int status = 0; unsigned long flags = 0; unsigned long mbx_flags = 0; unsigned long intr_iter; uint32_t stat; uint16_t hccr; /* Don't loop forever, interrupt are OFF */ intr_iter = 50; ha = (scsi_qla_host_t *) dev_id; if (!ha) { printk(KERN_INFO "%s(): NULL host pointer\n", __func__); return (IRQ_NONE); } reg = ha->iobase; spin_lock_irqsave(&ha->hardware_lock, flags); for (;;) { /* Relax CPU! */ if (!(intr_iter--)) break; if (IS_QLA2100(ha) || IS_QLA2200(ha)) { if ((RD_REG_WORD(®->istatus) & ISR_RISC_INT) == 0) break; if (RD_REG_WORD(®->semaphore) & BIT_0) { WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); /* Get mailbox data. */ mbx = RD_MAILBOX_REG(ha, reg, 0); if (mbx > 0x3fff && mbx < 0x8000) { qla2x00_mbx_completion(ha, (uint16_t)mbx); status |= MBX_INTERRUPT; } else if (mbx > 0x7fff && mbx < 0xc000) { qla2x00_async_event(ha, mbx); } else { /*EMPTY*/ DEBUG2(printk("scsi(%ld): Unrecognized " "interrupt type (%d)\n", ha->host_no, mbx)); } /* Release mailbox registers. */ WRT_REG_WORD(®->semaphore, 0); /* Workaround for ISP2100 chip. */ if (IS_QLA2100(ha)) RD_REG_WORD(®->semaphore); } else { qla2x00_process_response_queue(ha); WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); } } else /* IS_QLA23XX(ha) */ { stat = RD_REG_DWORD(®->u.isp2300.host_status); if ((stat & HSR_RISC_INT) == 0) break; mbx = MSW(stat); switch (stat & 0xff) { case 0x13: qla2x00_process_response_queue(ha); break; case 0x1: case 0x2: case 0x10: case 0x11: qla2x00_mbx_completion(ha, (uint16_t)mbx); status |= MBX_INTERRUPT; /* Release mailbox registers. */ WRT_REG_WORD(®->semaphore, 0); break; case 0x12: qla2x00_async_event(ha, mbx); break; case 0x15: mbx = mbx << 16 | MBA_CMPLT_1_16BIT; qla2x00_async_event(ha, mbx); break; case 0x16: mbx = mbx << 16 | MBA_SCSI_COMPLETION; qla2x00_async_event(ha, mbx); break; default: hccr = RD_REG_WORD(®->hccr); if (hccr & HCCR_RISC_PAUSE) { qla_printk(KERN_INFO, ha, "RISC paused, dumping HCCR=%x\n", hccr); /* * Issue a "HARD" reset in order for * the RISC interrupt bit to be * cleared. Schedule a big hammmer to * get out of the RISC PAUSED state. */ WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); RD_REG_WORD(®->hccr); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; } else { DEBUG2(printk("scsi(%ld): Unrecognized " "interrupt type (%d)\n", ha->host_no, stat & 0xff)); } break; } WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); } } spin_unlock_irqrestore(&ha->hardware_lock, flags); qla2x00_next(ha); ha->last_irq_cpu = smp_processor_id(); ha->total_isr_cnt++; if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && (status & MBX_INTERRUPT) && ha->flags.mbox_int) { /* There was a mailbox completion */ DEBUG3(printk("%s(%ld): Going to get mbx reg lock.\n", __func__, ha->host_no)); spin_lock_irqsave(&ha->mbx_reg_lock, mbx_flags); if (ha->mcp == NULL) { DEBUG3(printk("%s(%ld): Error mbx pointer.\n", __func__, ha->host_no)); } else { DEBUG3(printk("%s(%ld): Going to set mbx intr flags. " "cmd=%x.\n", __func__, ha->host_no, ha->mcp->mb[0])); } set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); DEBUG3(printk("%s(%ld): Going to wake up mbx function for " "completion.\n", __func__, ha->host_no)); up(&ha->mbx_intr_sem); DEBUG3(printk("%s(%ld): Going to release mbx reg lock.\n", __func__, ha->host_no)); spin_unlock_irqrestore(&ha->mbx_reg_lock, mbx_flags); } if (!list_empty(&ha->done_queue)) qla2x00_done(ha); /* Wakeup the DPC routine */ if ((!ha->flags.mbox_busy && (test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) || test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) || test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))) && ha->dpc_wait && !ha->dpc_active) { up(ha->dpc_wait); } return (IRQ_HANDLED); }
/** * qla2x00_async_event() - Process aynchronous events. * @ha: SCSI driver HA context * @mb: Mailbox registers (0 - 3) */ static void qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) { #define LS_UNKNOWN 2 static char *link_speeds[5] = { "1", "2", "?", "4", "10" }; char *link_speed; uint16_t handle_cnt; uint16_t cnt; uint32_t handles[5]; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; uint32_t rscn_entry, host_pid; uint8_t rscn_queue_index; /* Setup to process RIO completion. */ handle_cnt = 0; switch (mb[0]) { case MBA_SCSI_COMPLETION: handles[0] = le32_to_cpu((uint32_t)((mb[2] << 16) | mb[1])); handle_cnt = 1; break; case MBA_CMPLT_1_16BIT: handles[0] = mb[1]; handle_cnt = 1; mb[0] = MBA_SCSI_COMPLETION; break; case MBA_CMPLT_2_16BIT: handles[0] = mb[1]; handles[1] = mb[2]; handle_cnt = 2; mb[0] = MBA_SCSI_COMPLETION; break; case MBA_CMPLT_3_16BIT: handles[0] = mb[1]; handles[1] = mb[2]; handles[2] = mb[3]; handle_cnt = 3; mb[0] = MBA_SCSI_COMPLETION; break; case MBA_CMPLT_4_16BIT: handles[0] = mb[1]; handles[1] = mb[2]; handles[2] = mb[3]; handles[3] = (uint32_t)RD_MAILBOX_REG(ha, reg, 6); handle_cnt = 4; mb[0] = MBA_SCSI_COMPLETION; break; case MBA_CMPLT_5_16BIT: handles[0] = mb[1]; handles[1] = mb[2]; handles[2] = mb[3]; handles[3] = (uint32_t)RD_MAILBOX_REG(ha, reg, 6); handles[4] = (uint32_t)RD_MAILBOX_REG(ha, reg, 7); handle_cnt = 5; mb[0] = MBA_SCSI_COMPLETION; break; case MBA_CMPLT_2_32BIT: handles[0] = le32_to_cpu((uint32_t)((mb[2] << 16) | mb[1])); handles[1] = le32_to_cpu( ((uint32_t)(RD_MAILBOX_REG(ha, reg, 7) << 16)) | RD_MAILBOX_REG(ha, reg, 6)); handle_cnt = 2; mb[0] = MBA_SCSI_COMPLETION; break; default: break; } switch (mb[0]) { case MBA_SCSI_COMPLETION: /* Fast Post */ if (!ha->flags.online) break; for (cnt = 0; cnt < handle_cnt; cnt++) qla2x00_process_completed_request(ha, handles[cnt]); break; case MBA_RESET: /* Reset */ DEBUG2(printk("scsi(%ld): Asynchronous RESET.\n", ha->host_no)); set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); break; case MBA_SYSTEM_ERR: /* System Error */ mb[1] = RD_MAILBOX_REG(ha, reg, 1); mb[2] = RD_MAILBOX_REG(ha, reg, 2); mb[3] = RD_MAILBOX_REG(ha, reg, 3); qla_printk(KERN_INFO, ha, "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n", mb[1], mb[2], mb[3]); ha->isp_ops.fw_dump(ha, 1); if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { if (mb[1] == 0 && mb[2] == 0) { qla_printk(KERN_ERR, ha, "Unrecoverable Hardware Error: adapter " "marked OFFLINE!\n"); ha->flags.online = 0; } else set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); } else if (mb[1] == 0) { qla_printk(KERN_INFO, ha, "Unrecoverable Hardware Error: adapter marked " "OFFLINE!\n"); ha->flags.online = 0; } else set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */ DEBUG2(printk("scsi(%ld): ISP Request Transfer Error.\n", ha->host_no)); qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n"); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */ DEBUG2(printk("scsi(%ld): ISP Response Transfer Error.\n", ha->host_no)); qla_printk(KERN_WARNING, ha, "ISP Response Transfer Error.\n"); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; case MBA_WAKEUP_THRES: /* Request Queue Wake-up */ DEBUG2(printk("scsi(%ld): Asynchronous WAKEUP_THRES.\n", ha->host_no)); break; case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */ DEBUG2(printk("scsi(%ld): LIP occured (%x).\n", ha->host_no, mb[1])); qla_printk(KERN_INFO, ha, "LIP occured (%x).\n", mb[1]); if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); qla2x00_mark_all_devices_lost(ha, 1); } set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags); ha->flags.management_server_logged_in = 0; break; case MBA_LOOP_UP: /* Loop Up Event */ if (IS_QLA2100(ha) || IS_QLA2200(ha)) { link_speed = link_speeds[0]; ha->link_data_rate = LDR_1GB; } else { link_speed = link_speeds[LS_UNKNOWN]; if (mb[1] < 5) link_speed = link_speeds[mb[1]]; ha->link_data_rate = mb[1]; } DEBUG2(printk("scsi(%ld): Asynchronous LOOP UP (%s Gbps).\n", ha->host_no, link_speed)); qla_printk(KERN_INFO, ha, "LOOP UP detected (%s Gbps).\n", link_speed); ha->flags.management_server_logged_in = 0; break; case MBA_LOOP_DOWN: /* Loop Down Event */ DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN (%x).\n", ha->host_no, mb[1])); qla_printk(KERN_INFO, ha, "LOOP DOWN detected (%x).\n", mb[1]); if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); ha->device_flags |= DFLG_NO_CABLE; qla2x00_mark_all_devices_lost(ha, 1); } ha->flags.management_server_logged_in = 0; ha->link_data_rate = LDR_UNKNOWN; if (ql2xfdmienable) set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags); break; case MBA_LIP_RESET: /* LIP reset occurred */ DEBUG2(printk("scsi(%ld): Asynchronous LIP RESET (%x).\n", ha->host_no, mb[1])); qla_printk(KERN_INFO, ha, "LIP reset occured (%x).\n", mb[1]); if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); qla2x00_mark_all_devices_lost(ha, 1); } set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); ha->operating_mode = LOOP; ha->flags.management_server_logged_in = 0; break; case MBA_POINT_TO_POINT: /* Point-to-Point */ if (IS_QLA2100(ha)) break; DEBUG2(printk("scsi(%ld): Asynchronous P2P MODE received.\n", ha->host_no)); /* * Until there's a transition from loop down to loop up, treat * this as loop down only. */ if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); if (!atomic_read(&ha->loop_down_timer)) atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); qla2x00_mark_all_devices_lost(ha, 1); } if (!(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) { set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); } set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags); break; case MBA_CHG_IN_CONNECTION: /* Change in connection mode */ if (IS_QLA2100(ha)) break; DEBUG2(printk("scsi(%ld): Asynchronous Change In Connection " "received.\n", ha->host_no)); qla_printk(KERN_INFO, ha, "Configuration change detected: value=%x.\n", mb[1]); if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); if (!atomic_read(&ha->loop_down_timer)) atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); qla2x00_mark_all_devices_lost(ha, 1); } set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); break; case MBA_PORT_UPDATE: /* Port database update */ /* * If PORT UPDATE is global (recieved LIP_OCCURED/LIP_RESET * event etc. earlier indicating loop is down) then process * it. Otherwise ignore it and Wait for RSCN to come in. */ atomic_set(&ha->loop_down_timer, 0); if (atomic_read(&ha->loop_state) != LOOP_DOWN && atomic_read(&ha->loop_state) != LOOP_DEAD) { DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE " "ignored %04x/%04x/%04x.\n", ha->host_no, mb[1], mb[2], mb[3])); break; } DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE.\n", ha->host_no)); DEBUG(printk(KERN_INFO "scsi(%ld): Port database changed %04x %04x %04x.\n", ha->host_no, mb[1], mb[2], mb[3])); /* * Mark all devices as missing so we will login again. */ atomic_set(&ha->loop_state, LOOP_UP); qla2x00_mark_all_devices_lost(ha, 1); ha->flags.rscn_queue_overflow = 1; set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); break; case MBA_RSCN_UPDATE: /* State Change Registration */ DEBUG2(printk("scsi(%ld): Asynchronous RSCR UPDATE.\n", ha->host_no)); DEBUG(printk(KERN_INFO "scsi(%ld): RSCN database changed -- %04x %04x.\n", ha->host_no, mb[1], mb[2])); rscn_entry = (mb[1] << 16) | mb[2]; host_pid = (ha->d_id.b.domain << 16) | (ha->d_id.b.area << 8) | ha->d_id.b.al_pa; if (rscn_entry == host_pid) { DEBUG(printk(KERN_INFO "scsi(%ld): Ignoring RSCN update to local host " "port ID (%06x)\n", ha->host_no, host_pid)); break; } rscn_queue_index = ha->rscn_in_ptr + 1; if (rscn_queue_index == MAX_RSCN_COUNT) rscn_queue_index = 0; if (rscn_queue_index != ha->rscn_out_ptr) { ha->rscn_queue[ha->rscn_in_ptr] = rscn_entry; ha->rscn_in_ptr = rscn_queue_index; } else { ha->flags.rscn_queue_overflow = 1; } atomic_set(&ha->loop_state, LOOP_UPDATE); atomic_set(&ha->loop_down_timer, 0); ha->flags.management_server_logged_in = 0; set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); set_bit(RSCN_UPDATE, &ha->dpc_flags); break; /* case MBA_RIO_RESPONSE: */ case MBA_ZIO_RESPONSE: DEBUG2(printk("scsi(%ld): [R|Z]IO update completion.\n", ha->host_no)); DEBUG(printk(KERN_INFO "scsi(%ld): [R|Z]IO update completion.\n", ha->host_no)); if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) qla24xx_process_response_queue(ha); else qla2x00_process_response_queue(ha); break; case MBA_DISCARD_RND_FRAME: DEBUG2(printk("scsi(%ld): Discard RND Frame -- %04x %04x " "%04x.\n", ha->host_no, mb[1], mb[2], mb[3])); break; case MBA_TRACE_NOTIFICATION: DEBUG2(printk("scsi(%ld): Trace Notification -- %04x %04x.\n", ha->host_no, mb[1], mb[2])); break; } }
/** * qla2x00_status_entry() - Process a Status IOCB entry. * @ha: SCSI driver HA context * @pkt: Entry pointer */ static void qla2x00_status_entry(scsi_qla_host_t *ha, sts_entry_t *pkt) { int ret; unsigned b, t, l; srb_t *sp; os_lun_t *lq; os_tgt_t *tq; fc_port_t *fcport; struct scsi_cmnd *cp; uint16_t comp_status; uint16_t scsi_status; uint8_t lscsi_status; uint32_t resid; uint8_t sense_sz = 0; uint16_t rsp_info_len; /* Fast path completion. */ if (le16_to_cpu(pkt->comp_status) == CS_COMPLETE && (le16_to_cpu(pkt->scsi_status) & SS_MASK) == 0) { qla2x00_process_completed_request(ha, pkt->handle); return; } /* Validate handle. */ if (pkt->handle < MAX_OUTSTANDING_COMMANDS) { sp = ha->outstanding_cmds[pkt->handle]; ha->outstanding_cmds[pkt->handle] = 0; } else sp = NULL; if (sp == NULL) { DEBUG2(printk("scsi(%ld): Status Entry invalid handle.\n", ha->host_no)); qla_printk(KERN_WARNING, ha, "Status Entry invalid handle.\n"); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); if (ha->dpc_wait && !ha->dpc_active) up(ha->dpc_wait); return; } cp = sp->cmd; if (cp == NULL) { DEBUG2(printk("scsi(%ld): Command already returned back to OS " "pkt->handle=%d sp=%p sp->state:%d\n", ha->host_no, pkt->handle, sp, sp->state)); qla_printk(KERN_WARNING, ha, "Command is NULL: already returned to OS (sp=%p)\n", sp); return; } if (ha->actthreads) ha->actthreads--; if (sp->lun_queue == NULL) { DEBUG2(printk("scsi(%ld): Status Entry invalid lun pointer.\n", ha->host_no)); qla_printk(KERN_WARNING, ha, "Status Entry invalid lun pointer.\n"); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); if (ha->dpc_wait && !ha->dpc_active) up(ha->dpc_wait); return; } sp->lun_queue->out_cnt--; comp_status = le16_to_cpu(pkt->comp_status); /* Mask of reserved bits 12-15, before we examine the scsi status */ scsi_status = le16_to_cpu(pkt->scsi_status) & SS_MASK; lscsi_status = scsi_status & STATUS_MASK; CMD_ENTRY_STATUS(cp) = pkt->entry_status; CMD_COMPL_STATUS(cp) = comp_status; CMD_SCSI_STATUS(cp) = scsi_status; /* Generate LU queue on cntrl, target, LUN */ b = cp->device->channel; t = cp->device->id; l = cp->device->lun, tq = sp->tgt_queue; lq = sp->lun_queue; /* * If loop is in transient state Report DID_BUS_BUSY */ if ((comp_status != CS_COMPLETE || scsi_status != 0)) { if (!(sp->flags & SRB_IOCTL) && (atomic_read(&ha->loop_down_timer) || atomic_read(&ha->loop_state) != LOOP_READY)) { DEBUG2(printk("scsi(%ld:%d:%d:%d): Loop Not Ready - " "pid=%lx.\n", ha->host_no, b, t, l, cp->serial_number)); qla2x00_extend_timeout(cp, EXTEND_CMD_TIMEOUT); add_to_retry_queue(ha, sp); return; } } /* Check for any FCP transport errors. */ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) { rsp_info_len = le16_to_cpu(pkt->rsp_info_len); if (rsp_info_len > 3 && pkt->rsp_info[3]) { DEBUG2(printk("scsi(%ld:%d:%d:%d) FCP I/O protocol " "failure (%x/%02x%02x%02x%02x%02x%02x%02x%02x)..." "retrying command\n", ha->host_no, b, t, l, rsp_info_len, pkt->rsp_info[0], pkt->rsp_info[1], pkt->rsp_info[2], pkt->rsp_info[3], pkt->rsp_info[4], pkt->rsp_info[5], pkt->rsp_info[6], pkt->rsp_info[7])); cp->result = DID_BUS_BUSY << 16; add_to_done_queue(ha, sp); return; } } /* * Based on Host and scsi status generate status code for Linux */ switch (comp_status) { case CS_COMPLETE: if (scsi_status == 0) { cp->result = DID_OK << 16; break; } if (lscsi_status == SS_BUSY_CONDITION) { cp->result = DID_BUS_BUSY << 16 | lscsi_status; break; } cp->result = DID_OK << 16 | lscsi_status; if (lscsi_status != SS_CHECK_CONDITION) break; /* * Copy Sense Data into sense buffer */ memset(cp->sense_buffer, 0, sizeof(cp->sense_buffer)); if (!(scsi_status & SS_SENSE_LEN_VALID)) break; if (le16_to_cpu(pkt->req_sense_length) < sizeof(cp->sense_buffer)) sense_sz = le16_to_cpu(pkt->req_sense_length); else sense_sz = sizeof(cp->sense_buffer) - 1; CMD_ACTUAL_SNSLEN(cp) = sense_sz; sp->request_sense_length = sense_sz; sp->request_sense_ptr = cp->sense_buffer; if (sp->request_sense_length > 32) sense_sz = 32; memcpy(cp->sense_buffer, pkt->req_sense_data, sense_sz); sp->request_sense_ptr += sense_sz; sp->request_sense_length -= sense_sz; if (sp->request_sense_length != 0) ha->status_srb = sp; if (!(sp->flags & SRB_IOCTL) && qla2x00_check_sense(cp, lq) == QLA_SUCCESS) { /* Throw away status_cont if any */ ha->status_srb = NULL; add_to_scsi_retry_queue(ha, sp); return; } DEBUG5(printk("%s(): Check condition Sense data, " "scsi(%ld:%d:%d:%d) cmd=%p pid=%ld\n", __func__, ha->host_no, b, t, l, cp, cp->serial_number)); if (sense_sz) DEBUG5(qla2x00_dump_buffer(cp->sense_buffer, CMD_ACTUAL_SNSLEN(cp))); break; case CS_DATA_UNDERRUN: DEBUG2(printk(KERN_INFO "scsi(%ld:%d:%d) UNDERRUN status detected 0x%x-0x%x.\n", ha->host_no, t, l, comp_status, scsi_status)); resid = le32_to_cpu(pkt->residual_length); CMD_RESID_LEN(cp) = resid; /* * Check to see if SCSI Status is non zero. If so report SCSI * Status. */ if (lscsi_status != 0) { if (lscsi_status == SS_BUSY_CONDITION) { cp->result = DID_BUS_BUSY << 16 | lscsi_status; break; } cp->result = DID_OK << 16 | lscsi_status; if (lscsi_status != SS_CHECK_CONDITION) break; /* Copy Sense Data into sense buffer */ memset(cp->sense_buffer, 0, sizeof(cp->sense_buffer)); if (!(scsi_status & SS_SENSE_LEN_VALID)) break; if (le16_to_cpu(pkt->req_sense_length) < sizeof(cp->sense_buffer)) sense_sz = le16_to_cpu(pkt->req_sense_length); else sense_sz = sizeof(cp->sense_buffer) - 1; CMD_ACTUAL_SNSLEN(cp) = sense_sz; sp->request_sense_length = sense_sz; sp->request_sense_ptr = cp->sense_buffer; if (sp->request_sense_length > 32) sense_sz = 32; memcpy(cp->sense_buffer, pkt->req_sense_data, sense_sz); sp->request_sense_ptr += sense_sz; sp->request_sense_length -= sense_sz; if (sp->request_sense_length != 0) ha->status_srb = sp; if (!(sp->flags & SRB_IOCTL) && (qla2x00_check_sense(cp, lq) == QLA_SUCCESS)) { ha->status_srb = NULL; add_to_scsi_retry_queue(ha, sp); return; } DEBUG5(printk("%s(): Check condition Sense data, " "scsi(%ld:%d:%d:%d) cmd=%p pid=%ld\n", __func__, ha->host_no, b, t, l, cp, cp->serial_number)); if (sense_sz) DEBUG5(qla2x00_dump_buffer(cp->sense_buffer, CMD_ACTUAL_SNSLEN(cp))); } 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 (!(scsi_status & SS_RESIDUAL_UNDER)) { DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped " "frame(s) detected (%x of %x bytes)..." "retrying command.\n", ha->host_no, b, t, l, resid, cp->request_bufflen)); cp->result = DID_BUS_BUSY << 16; ha->dropped_frame_error_cnt++; break; } /* Handle mid-layer underflow */ cp->resid = resid; if ((unsigned)(cp->request_bufflen - resid) < cp->underflow) { qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d:%d): Mid-layer underflow " "detected (%x of %x bytes)...returning " "error status.\n", ha->host_no, b, t, l, resid, cp->request_bufflen); cp->result = DID_ERROR << 16; break; } /* Everybody online, looking good... */ cp->result = DID_OK << 16; } break; case CS_DATA_OVERRUN: DEBUG2(printk(KERN_INFO "scsi(%ld:%d:%d): OVERRUN status detected 0x%x-0x%x\n", ha->host_no, t, l, comp_status, scsi_status)); DEBUG2(printk(KERN_INFO "CDB: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", cp->cmnd[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3], cp->cmnd[4], cp->cmnd[5])); DEBUG2(printk(KERN_INFO "PID=0x%lx req=0x%x xtra=0x%x -- returning DID_ERROR " "status!\n", cp->serial_number, cp->request_bufflen, le32_to_cpu(pkt->residual_length))); cp->result = DID_ERROR << 16; break; case CS_PORT_LOGGED_OUT: case CS_PORT_CONFIG_CHG: case CS_PORT_BUSY: case CS_INCOMPLETE: case CS_PORT_UNAVAILABLE: /* * If the port is in Target Down state, return all IOs for this * Target with DID_NO_CONNECT ELSE Queue the IOs in the * retry_queue. */ fcport = sp->fclun->fcport; DEBUG2(printk("scsi(%ld:%d:%d): status_entry: Port Down " "pid=%ld, compl status=0x%x, port state=0x%x\n", ha->host_no, t, l, cp->serial_number, comp_status, atomic_read(&fcport->state))); if ((sp->flags & SRB_IOCTL) || atomic_read(&fcport->state) == FCS_DEVICE_DEAD) { cp->result = DID_NO_CONNECT << 16; if (atomic_read(&ha->loop_state) == LOOP_DOWN) sp->err_id = SRB_ERR_LOOP; else sp->err_id = SRB_ERR_PORT; add_to_done_queue(ha, sp); } else { qla2x00_extend_timeout(cp, EXTEND_CMD_TIMEOUT); add_to_retry_queue(ha, sp); } if (atomic_read(&fcport->state) == FCS_ONLINE) { qla2x00_mark_device_lost(ha, fcport, 1); } return; break; case CS_RESET: DEBUG2(printk(KERN_INFO "scsi(%ld): RESET status detected 0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); if (sp->flags & SRB_IOCTL) { cp->result = DID_RESET << 16; } else { qla2x00_extend_timeout(cp, EXTEND_CMD_TIMEOUT); add_to_retry_queue(ha, sp); return; } break; case CS_ABORTED: /* * hv2.19.12 - DID_ABORT does not retry the request if we * aborted this request then abort otherwise it must be a * reset. */ DEBUG2(printk(KERN_INFO "scsi(%ld): ABORT status detected 0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); cp->result = DID_RESET << 16; break; case CS_TIMEOUT: DEBUG2(printk(KERN_INFO "scsi(%ld:%d:%d:%d): TIMEOUT status detected 0x%x-0x%x.\n", ha->host_no, b, t, l, comp_status, scsi_status)); cp->result = DID_BUS_BUSY << 16; fcport = lq->fclun->fcport; /* Check to see if logout occurred */ if ((le16_to_cpu(pkt->status_flags) & SF_LOGOUT_SENT)) { qla2x00_mark_device_lost(ha, fcport, 1); } break; case CS_QUEUE_FULL: DEBUG2(printk(KERN_INFO "scsi(%ld): QUEUE FULL status detected 0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); /* SCSI Mid-Layer handles device queue full */ cp->result = DID_OK << 16 | lscsi_status; /* TODO: ??? */ /* Adjust queue depth */ ret = scsi_track_queue_full(cp->device, sp->lun_queue->out_cnt - 1); if (ret) { qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d:%d): Queue depth adjusted to %d.\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, ret); } break; default: DEBUG3(printk("scsi(%ld): Error detected (unknown status) " "0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); qla_printk(KERN_INFO, ha, "Unknown status detected 0x%x-0x%x.\n", comp_status, scsi_status); cp->result = DID_ERROR << 16; break; } /* Place command on done queue. */ if (ha->status_srb == NULL) add_to_done_queue(ha, sp); }
/** * qla2x00_status_entry() - Process a Status IOCB entry. * @ha: SCSI driver HA context * @pkt: Entry pointer */ static void qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) { srb_t *sp; fc_port_t *fcport; struct scsi_cmnd *cp; sts_entry_t *sts; struct sts_entry_24xx *sts24; uint16_t comp_status; uint16_t scsi_status; uint8_t lscsi_status; int32_t resid; uint32_t sense_len, rsp_info_len, resid_len, fw_resid_len; uint8_t *rsp_info, *sense_data; sts = (sts_entry_t *) pkt; sts24 = (struct sts_entry_24xx *) pkt; if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { comp_status = le16_to_cpu(sts24->comp_status); scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK; } else { comp_status = le16_to_cpu(sts->comp_status); scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK; } /* Fast path completion. */ if (comp_status == CS_COMPLETE && scsi_status == 0) { qla2x00_process_completed_request(ha, sts->handle); return; } /* Validate handle. */ if (sts->handle < MAX_OUTSTANDING_COMMANDS) { sp = ha->outstanding_cmds[sts->handle]; ha->outstanding_cmds[sts->handle] = NULL; } else sp = NULL; if (sp == NULL) { DEBUG2(printk("scsi(%ld): Status Entry invalid handle.\n", ha->host_no)); qla_printk(KERN_WARNING, ha, "Status Entry invalid handle.\n"); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); qla2xxx_wake_dpc(ha); return; } cp = sp->cmd; if (cp == NULL) { DEBUG2(printk("scsi(%ld): Command already returned back to OS " "pkt->handle=%d sp=%p.\n", ha->host_no, sts->handle, sp)); qla_printk(KERN_WARNING, ha, "Command is NULL: already returned to OS (sp=%p)\n", sp); return; } lscsi_status = scsi_status & STATUS_MASK; CMD_ENTRY_STATUS(cp) = sts->entry_status; CMD_COMPL_STATUS(cp) = comp_status; CMD_SCSI_STATUS(cp) = scsi_status; fcport = sp->fcport; sense_len = rsp_info_len = resid_len = fw_resid_len = 0; if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { sense_len = le32_to_cpu(sts24->sense_len); rsp_info_len = le32_to_cpu(sts24->rsp_data_len); resid_len = le32_to_cpu(sts24->rsp_residual_count); fw_resid_len = le32_to_cpu(sts24->residual_len); rsp_info = sts24->data; sense_data = sts24->data; host_to_fcp_swap(sts24->data, sizeof(sts24->data)); } else { sense_len = le16_to_cpu(sts->req_sense_length); rsp_info_len = le16_to_cpu(sts->rsp_info_len); resid_len = le32_to_cpu(sts->residual_length); rsp_info = sts->rsp_info; sense_data = sts->req_sense_data; } /* Check for any FCP transport errors. */ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) { /* Sense data lies beyond any FCP RESPONSE data. */ if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) sense_data += rsp_info_len; if (rsp_info_len > 3 && rsp_info[3]) { DEBUG2(printk("scsi(%ld:%d:%d:%d) FCP I/O protocol " "failure (%x/%02x%02x%02x%02x%02x%02x%02x%02x)..." "retrying command\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, rsp_info_len, rsp_info[0], rsp_info[1], rsp_info[2], rsp_info[3], rsp_info[4], rsp_info[5], rsp_info[6], rsp_info[7])); cp->result = DID_BUS_BUSY << 16; qla2x00_sp_compl(ha, sp); return; } } /* * Based on Host and scsi status generate status code for Linux */ switch (comp_status) { case CS_COMPLETE: if (scsi_status == 0) { cp->result = DID_OK << 16; break; } if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER)) { resid = resid_len; cp->resid = resid; CMD_RESID_LEN(cp) = resid; if (!lscsi_status && ((unsigned)(cp->request_bufflen - resid) < cp->underflow)) { qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d:%d): Mid-layer underflow " "detected (%x of %x bytes)...returning " "error status.\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, resid, cp->request_bufflen); cp->result = DID_ERROR << 16; break; } } cp->result = DID_OK << 16 | lscsi_status; if (lscsi_status != SS_CHECK_CONDITION) break; /* Copy Sense Data into sense buffer. */ memset(cp->sense_buffer, 0, sizeof(cp->sense_buffer)); if (!(scsi_status & SS_SENSE_LEN_VALID)) break; if (sense_len >= sizeof(cp->sense_buffer)) sense_len = sizeof(cp->sense_buffer); CMD_ACTUAL_SNSLEN(cp) = sense_len; sp->request_sense_length = sense_len; sp->request_sense_ptr = cp->sense_buffer; if (sp->request_sense_length > 32) sense_len = 32; memcpy(cp->sense_buffer, sense_data, sense_len); sp->request_sense_ptr += sense_len; sp->request_sense_length -= sense_len; if (sp->request_sense_length != 0) ha->status_srb = sp; DEBUG5(printk("%s(): Check condition Sense data, " "scsi(%ld:%d:%d:%d) cmd=%p pid=%ld\n", __func__, ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, cp, cp->serial_number)); if (sense_len) DEBUG5(qla2x00_dump_buffer(cp->sense_buffer, CMD_ACTUAL_SNSLEN(cp))); break; case CS_DATA_UNDERRUN: resid = resid_len; /* Use F/W calculated residual length. */ if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) resid = fw_resid_len; if (scsi_status & SS_RESIDUAL_UNDER) { cp->resid = resid; CMD_RESID_LEN(cp) = resid; } else { DEBUG2(printk(KERN_INFO "scsi(%ld:%d:%d) UNDERRUN status detected " "0x%x-0x%x. resid=0x%x fw_resid=0x%x cdb=0x%x " "os_underflow=0x%x\n", ha->host_no, cp->device->id, cp->device->lun, comp_status, scsi_status, resid_len, resid, cp->cmnd[0], cp->underflow)); } /* * Check to see if SCSI Status is non zero. If so report SCSI * Status. */ if (lscsi_status != 0) { cp->result = DID_OK << 16 | lscsi_status; if (lscsi_status != SS_CHECK_CONDITION) break; /* Copy Sense Data into sense buffer */ memset(cp->sense_buffer, 0, sizeof(cp->sense_buffer)); if (!(scsi_status & SS_SENSE_LEN_VALID)) break; if (sense_len >= sizeof(cp->sense_buffer)) sense_len = sizeof(cp->sense_buffer); CMD_ACTUAL_SNSLEN(cp) = sense_len; sp->request_sense_length = sense_len; sp->request_sense_ptr = cp->sense_buffer; if (sp->request_sense_length > 32) sense_len = 32; memcpy(cp->sense_buffer, sense_data, sense_len); sp->request_sense_ptr += sense_len; sp->request_sense_length -= sense_len; if (sp->request_sense_length != 0) ha->status_srb = sp; DEBUG5(printk("%s(): Check condition Sense data, " "scsi(%ld:%d:%d:%d) cmd=%p pid=%ld\n", __func__, ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, cp, cp->serial_number)); if (sense_len) DEBUG5(qla2x00_dump_buffer(cp->sense_buffer, CMD_ACTUAL_SNSLEN(cp))); } 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 (!(scsi_status & SS_RESIDUAL_UNDER)) { DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped " "frame(s) detected (%x of %x bytes)..." "retrying command.\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, resid, cp->request_bufflen)); cp->result = DID_BUS_BUSY << 16; break; } /* Handle mid-layer underflow */ if ((unsigned)(cp->request_bufflen - resid) < cp->underflow) { qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d:%d): Mid-layer underflow " "detected (%x of %x bytes)...returning " "error status.\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, resid, cp->request_bufflen); cp->result = DID_ERROR << 16; break; } /* Everybody online, looking good... */ cp->result = DID_OK << 16; } break; case CS_DATA_OVERRUN: DEBUG2(printk(KERN_INFO "scsi(%ld:%d:%d): OVERRUN status detected 0x%x-0x%x\n", ha->host_no, cp->device->id, cp->device->lun, comp_status, scsi_status)); DEBUG2(printk(KERN_INFO "CDB: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", cp->cmnd[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3], cp->cmnd[4], cp->cmnd[5])); DEBUG2(printk(KERN_INFO "PID=0x%lx req=0x%x xtra=0x%x -- returning DID_ERROR " "status!\n", cp->serial_number, cp->request_bufflen, resid_len)); cp->result = DID_ERROR << 16; break; case CS_PORT_LOGGED_OUT: case CS_PORT_CONFIG_CHG: case CS_PORT_BUSY: case CS_INCOMPLETE: case CS_PORT_UNAVAILABLE: /* * If the port is in Target Down state, return all IOs for this * Target with DID_NO_CONNECT ELSE Queue the IOs in the * retry_queue. */ DEBUG2(printk("scsi(%ld:%d:%d): status_entry: Port Down " "pid=%ld, compl status=0x%x, port state=0x%x\n", ha->host_no, cp->device->id, cp->device->lun, cp->serial_number, comp_status, atomic_read(&fcport->state))); cp->result = DID_BUS_BUSY << 16; if (atomic_read(&fcport->state) == FCS_ONLINE) { qla2x00_mark_device_lost(ha, fcport, 1, 1); } break; case CS_RESET: DEBUG2(printk(KERN_INFO "scsi(%ld): RESET status detected 0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); cp->result = DID_RESET << 16; break; case CS_ABORTED: /* * hv2.19.12 - DID_ABORT does not retry the request if we * aborted this request then abort otherwise it must be a * reset. */ DEBUG2(printk(KERN_INFO "scsi(%ld): ABORT status detected 0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); cp->result = DID_RESET << 16; break; case CS_TIMEOUT: cp->result = DID_BUS_BUSY << 16; if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { DEBUG2(printk(KERN_INFO "scsi(%ld:%d:%d:%d): TIMEOUT status detected " "0x%x-0x%x\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, comp_status, scsi_status)); break; } DEBUG2(printk(KERN_INFO "scsi(%ld:%d:%d:%d): TIMEOUT status detected 0x%x-0x%x " "sflags=%x.\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, comp_status, scsi_status, le16_to_cpu(sts->status_flags))); /* Check to see if logout occurred. */ if ((le16_to_cpu(sts->status_flags) & SF_LOGOUT_SENT)) qla2x00_mark_device_lost(ha, fcport, 1, 1); break; case CS_QUEUE_FULL: DEBUG2(printk(KERN_INFO "scsi(%ld): QUEUE FULL status detected 0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); /* SCSI Mid-Layer handles device queue full */ cp->result = DID_OK << 16 | lscsi_status; break; default: DEBUG3(printk("scsi(%ld): Error detected (unknown status) " "0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); qla_printk(KERN_INFO, ha, "Unknown status detected 0x%x-0x%x.\n", comp_status, scsi_status); cp->result = DID_ERROR << 16; break; } /* Place command on done queue. */ if (ha->status_srb == NULL) qla2x00_sp_compl(ha, sp); }
/** * qla2100_fw_dump() - Dumps binary data from the 2100/2200 firmware. * @ha: HA context * @hardware_locked: Called with the hardware_lock */ void qla2100_fw_dump(scsi_qla_host_t *ha, int hardware_locked) { int rval; uint32_t cnt, timer; uint16_t risc_address; uint16_t mb0, mb2; device_reg_t __iomem *reg = ha->iobase; uint16_t __iomem *dmp_reg; unsigned long flags; struct qla2100_fw_dump *fw; risc_address = 0; mb0 = mb2 = 0; flags = 0; if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (ha->fw_dump != NULL) { qla_printk(KERN_WARNING, ha, "Firmware has been previously dumped (%p) -- ignoring " "request...\n", ha->fw_dump); goto qla2100_fw_dump_failed; } /* Allocate (large) dump buffer. */ ha->fw_dump_order = get_order(sizeof(struct qla2100_fw_dump)); ha->fw_dump = (struct qla2100_fw_dump *) __get_free_pages(GFP_ATOMIC, ha->fw_dump_order); if (ha->fw_dump == NULL) { qla_printk(KERN_WARNING, ha, "Unable to allocated memory for firmware dump (%d/%Zd).\n", ha->fw_dump_order, sizeof(struct qla2100_fw_dump)); goto qla2100_fw_dump_failed; } fw = ha->fw_dump; rval = QLA_SUCCESS; fw->hccr = RD_REG_WORD(®->hccr); /* Pause RISC. */ WRT_REG_WORD(®->hccr, HCCR_PAUSE_RISC); for (cnt = 30000; (RD_REG_WORD(®->hccr) & HCCR_RISC_PAUSE) == 0 && rval == QLA_SUCCESS; cnt--) { if (cnt) udelay(100); else rval = QLA_FUNCTION_TIMEOUT; } if (rval == QLA_SUCCESS) { dmp_reg = (uint16_t __iomem *)(reg + 0); for (cnt = 0; cnt < sizeof(fw->pbiu_reg) / 2; cnt++) fw->pbiu_reg[cnt] = RD_REG_WORD(dmp_reg++); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x10); for (cnt = 0; cnt < ha->mbx_count; cnt++) { if (cnt == 8) { dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0xe0); } fw->mailbox_reg[cnt] = RD_REG_WORD(dmp_reg++); } dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x20); for (cnt = 0; cnt < sizeof(fw->dma_reg) / 2; cnt++) fw->dma_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->ctrl_status, 0x00); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0xA0); for (cnt = 0; cnt < sizeof(fw->risc_hdw_reg) / 2; cnt++) fw->risc_hdw_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2000); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp0_reg) / 2; cnt++) fw->risc_gp0_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2100); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp1_reg) / 2; cnt++) fw->risc_gp1_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2200); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp2_reg) / 2; cnt++) fw->risc_gp2_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2300); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp3_reg) / 2; cnt++) fw->risc_gp3_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2400); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp4_reg) / 2; cnt++) fw->risc_gp4_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2500); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp5_reg) / 2; cnt++) fw->risc_gp5_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2600); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp6_reg) / 2; cnt++) fw->risc_gp6_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->pcr, 0x2700); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->risc_gp7_reg) / 2; cnt++) fw->risc_gp7_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->ctrl_status, 0x10); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->frame_buf_hdw_reg) / 2; cnt++) fw->frame_buf_hdw_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->ctrl_status, 0x20); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->fpm_b0_reg) / 2; cnt++) fw->fpm_b0_reg[cnt] = RD_REG_WORD(dmp_reg++); WRT_REG_WORD(®->ctrl_status, 0x30); dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); for (cnt = 0; cnt < sizeof(fw->fpm_b1_reg) / 2; cnt++) fw->fpm_b1_reg[cnt] = RD_REG_WORD(dmp_reg++); /* Reset the ISP. */ WRT_REG_WORD(®->ctrl_status, CSR_ISP_SOFT_RESET); } for (cnt = 30000; RD_MAILBOX_REG(ha, reg, 0) != 0 && rval == QLA_SUCCESS; cnt--) { if (cnt) udelay(100); else rval = QLA_FUNCTION_TIMEOUT; } /* Pause RISC. */ if (rval == QLA_SUCCESS && (IS_QLA2200(ha) || (IS_QLA2100(ha) && (RD_REG_WORD(®->mctr) & (BIT_1 | BIT_0)) != 0))) { WRT_REG_WORD(®->hccr, HCCR_PAUSE_RISC); for (cnt = 30000; (RD_REG_WORD(®->hccr) & HCCR_RISC_PAUSE) == 0 && rval == QLA_SUCCESS; cnt--) { if (cnt) udelay(100); else rval = QLA_FUNCTION_TIMEOUT; } if (rval == QLA_SUCCESS) { /* Set memory configuration and timing. */ if (IS_QLA2100(ha)) WRT_REG_WORD(®->mctr, 0xf1); else WRT_REG_WORD(®->mctr, 0xf2); RD_REG_WORD(®->mctr); /* PCI Posting. */ /* Release RISC. */ WRT_REG_WORD(®->hccr, HCCR_RELEASE_RISC); } } if (rval == QLA_SUCCESS) { /* Get RISC SRAM. */ risc_address = 0x1000; WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_WORD); clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); } for (cnt = 0; cnt < sizeof(fw->risc_ram) / 2 && rval == QLA_SUCCESS; cnt++, risc_address++) { WRT_MAILBOX_REG(ha, reg, 1, risc_address); WRT_REG_WORD(®->hccr, HCCR_SET_HOST_INT); for (timer = 6000000; timer != 0; timer--) { /* Check for pending interrupts. */ if (RD_REG_WORD(®->istatus) & ISR_RISC_INT) { if (RD_REG_WORD(®->semaphore) & BIT_0) { set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); mb0 = RD_MAILBOX_REG(ha, reg, 0); mb2 = RD_MAILBOX_REG(ha, reg, 2); WRT_REG_WORD(®->semaphore, 0); WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); break; } WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); } udelay(5); } if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) { rval = mb0 & MBS_MASK; fw->risc_ram[cnt] = mb2; } else { rval = QLA_FUNCTION_FAILED; } } if (rval != QLA_SUCCESS) { qla_printk(KERN_WARNING, ha, "Failed to dump firmware (%x)!!!\n", rval); free_pages((unsigned long)ha->fw_dump, ha->fw_dump_order); ha->fw_dump = NULL; } else { qla_printk(KERN_INFO, ha, "Firmware dump saved to temp buffer (%ld/%p).\n", ha->host_no, ha->fw_dump); } qla2100_fw_dump_failed: if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); }
/** * qla2100_fw_dump() - Dumps binary data from the 2100/2200 firmware. * @ha: HA context * @hardware_locked: Called with the hardware_lock */ void qla2100_fw_dump(scsi_qla_host_t *ha, int hardware_locked) { int rval; uint32_t cnt, timer; uint16_t risc_address; uint16_t mb0, mb2; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; uint16_t __iomem *dmp_reg; unsigned long flags; struct qla2100_fw_dump *fw; risc_address = 0; mb0 = mb2 = 0; flags = 0; if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (!ha->fw_dump) { qla_printk(KERN_WARNING, ha, "No buffer available for dump!!!\n"); goto qla2100_fw_dump_failed; } if (ha->fw_dumped) { qla_printk(KERN_WARNING, ha, "Firmware has been previously dumped (%p) -- ignoring " "request...\n", ha->fw_dump); goto qla2100_fw_dump_failed; } fw = &ha->fw_dump->isp.isp21; qla2xxx_prep_dump(ha, ha->fw_dump); rval = QLA_SUCCESS; fw->hccr = htons(RD_REG_WORD(®->hccr)); /* Pause RISC. */ WRT_REG_WORD(®->hccr, HCCR_PAUSE_RISC); for (cnt = 30000; (RD_REG_WORD(®->hccr) & HCCR_RISC_PAUSE) == 0 && rval == QLA_SUCCESS; cnt--) { if (cnt) udelay(100); else rval = QLA_FUNCTION_TIMEOUT; } if (rval == QLA_SUCCESS) { dmp_reg = ®->flash_address; for (cnt = 0; cnt < sizeof(fw->pbiu_reg) / 2; cnt++) fw->pbiu_reg[cnt] = htons(RD_REG_WORD(dmp_reg++)); dmp_reg = ®->u.isp2100.mailbox0; for (cnt = 0; cnt < ha->mbx_count; cnt++) { if (cnt == 8) dmp_reg = ®->u_end.isp2200.mailbox8; fw->mailbox_reg[cnt] = htons(RD_REG_WORD(dmp_reg++)); } dmp_reg = ®->u.isp2100.unused_2[0]; for (cnt = 0; cnt < sizeof(fw->dma_reg) / 2; cnt++) fw->dma_reg[cnt] = htons(RD_REG_WORD(dmp_reg++)); WRT_REG_WORD(®->ctrl_status, 0x00); dmp_reg = ®->risc_hw; for (cnt = 0; cnt < sizeof(fw->risc_hdw_reg) / 2; cnt++) fw->risc_hdw_reg[cnt] = htons(RD_REG_WORD(dmp_reg++)); WRT_REG_WORD(®->pcr, 0x2000); qla2xxx_read_window(reg, 16, fw->risc_gp0_reg); WRT_REG_WORD(®->pcr, 0x2100); qla2xxx_read_window(reg, 16, fw->risc_gp1_reg); WRT_REG_WORD(®->pcr, 0x2200); qla2xxx_read_window(reg, 16, fw->risc_gp2_reg); WRT_REG_WORD(®->pcr, 0x2300); qla2xxx_read_window(reg, 16, fw->risc_gp3_reg); WRT_REG_WORD(®->pcr, 0x2400); qla2xxx_read_window(reg, 16, fw->risc_gp4_reg); WRT_REG_WORD(®->pcr, 0x2500); qla2xxx_read_window(reg, 16, fw->risc_gp5_reg); WRT_REG_WORD(®->pcr, 0x2600); qla2xxx_read_window(reg, 16, fw->risc_gp6_reg); WRT_REG_WORD(®->pcr, 0x2700); qla2xxx_read_window(reg, 16, fw->risc_gp7_reg); WRT_REG_WORD(®->ctrl_status, 0x10); qla2xxx_read_window(reg, 16, fw->frame_buf_hdw_reg); WRT_REG_WORD(®->ctrl_status, 0x20); qla2xxx_read_window(reg, 64, fw->fpm_b0_reg); WRT_REG_WORD(®->ctrl_status, 0x30); qla2xxx_read_window(reg, 64, fw->fpm_b1_reg); /* Reset the ISP. */ WRT_REG_WORD(®->ctrl_status, CSR_ISP_SOFT_RESET); } for (cnt = 30000; RD_MAILBOX_REG(ha, reg, 0) != 0 && rval == QLA_SUCCESS; cnt--) { if (cnt) udelay(100); else rval = QLA_FUNCTION_TIMEOUT; } /* Pause RISC. */ if (rval == QLA_SUCCESS && (IS_QLA2200(ha) || (IS_QLA2100(ha) && (RD_REG_WORD(®->mctr) & (BIT_1 | BIT_0)) != 0))) { WRT_REG_WORD(®->hccr, HCCR_PAUSE_RISC); for (cnt = 30000; (RD_REG_WORD(®->hccr) & HCCR_RISC_PAUSE) == 0 && rval == QLA_SUCCESS; cnt--) { if (cnt) udelay(100); else rval = QLA_FUNCTION_TIMEOUT; } if (rval == QLA_SUCCESS) { /* Set memory configuration and timing. */ if (IS_QLA2100(ha)) WRT_REG_WORD(®->mctr, 0xf1); else WRT_REG_WORD(®->mctr, 0xf2); RD_REG_WORD(®->mctr); /* PCI Posting. */ /* Release RISC. */ WRT_REG_WORD(®->hccr, HCCR_RELEASE_RISC); } } if (rval == QLA_SUCCESS) { /* Get RISC SRAM. */ risc_address = 0x1000; WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_WORD); clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); } for (cnt = 0; cnt < sizeof(fw->risc_ram) / 2 && rval == QLA_SUCCESS; cnt++, risc_address++) { WRT_MAILBOX_REG(ha, reg, 1, risc_address); WRT_REG_WORD(®->hccr, HCCR_SET_HOST_INT); for (timer = 6000000; timer != 0; timer--) { /* Check for pending interrupts. */ if (RD_REG_WORD(®->istatus) & ISR_RISC_INT) { if (RD_REG_WORD(®->semaphore) & BIT_0) { set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); mb0 = RD_MAILBOX_REG(ha, reg, 0); mb2 = RD_MAILBOX_REG(ha, reg, 2); WRT_REG_WORD(®->semaphore, 0); WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); break; } WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD(®->hccr); } udelay(5); } if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) { rval = mb0 & MBS_MASK; fw->risc_ram[cnt] = htons(mb2); } else { rval = QLA_FUNCTION_FAILED; } } if (rval == QLA_SUCCESS) qla2xxx_copy_queues(ha, &fw->risc_ram[cnt]); if (rval != QLA_SUCCESS) { qla_printk(KERN_WARNING, ha, "Failed to dump firmware (%x)!!!\n", rval); ha->fw_dumped = 0; } else { qla_printk(KERN_INFO, ha, "Firmware dump saved to temp buffer (%ld/%p).\n", ha->host_no, ha->fw_dump); ha->fw_dumped = 1; } qla2100_fw_dump_failed: if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); }