162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * QLogic Fibre Channel HBA Driver 462306a36Sopenharmony_ci * Copyright (c) 2003-2014 QLogic Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "qla_target.h" 862306a36Sopenharmony_ci/** 962306a36Sopenharmony_ci * qla24xx_calc_iocbs() - Determine number of Command Type 3 and 1062306a36Sopenharmony_ci * Continuation Type 1 IOCBs to allocate. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * @vha: HA context 1362306a36Sopenharmony_ci * @dsds: number of data segment descriptors needed 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Returns the number of IOCB entries needed to store @dsds. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_cistatic inline uint16_t 1862306a36Sopenharmony_ciqla24xx_calc_iocbs(scsi_qla_host_t *vha, uint16_t dsds) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci uint16_t iocbs; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci iocbs = 1; 2362306a36Sopenharmony_ci if (dsds > 1) { 2462306a36Sopenharmony_ci iocbs += (dsds - 1) / 5; 2562306a36Sopenharmony_ci if ((dsds - 1) % 5) 2662306a36Sopenharmony_ci iocbs++; 2762306a36Sopenharmony_ci } 2862306a36Sopenharmony_ci return iocbs; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* 3262306a36Sopenharmony_ci * qla2x00_debounce_register 3362306a36Sopenharmony_ci * Debounce register. 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Input: 3662306a36Sopenharmony_ci * port = register address. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Returns: 3962306a36Sopenharmony_ci * register value. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_cistatic __inline__ uint16_t 4262306a36Sopenharmony_ciqla2x00_debounce_register(volatile __le16 __iomem *addr) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci volatile uint16_t first; 4562306a36Sopenharmony_ci volatile uint16_t second; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci do { 4862306a36Sopenharmony_ci first = rd_reg_word(addr); 4962306a36Sopenharmony_ci barrier(); 5062306a36Sopenharmony_ci cpu_relax(); 5162306a36Sopenharmony_ci second = rd_reg_word(addr); 5262306a36Sopenharmony_ci } while (first != second); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return (first); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic inline void 5862306a36Sopenharmony_ciqla2x00_poll(struct rsp_que *rsp) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct qla_hw_data *ha = rsp->hw; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (IS_P3P_TYPE(ha)) 6362306a36Sopenharmony_ci qla82xx_poll(0, rsp); 6462306a36Sopenharmony_ci else 6562306a36Sopenharmony_ci ha->isp_ops->intr_handler(0, rsp); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic inline uint8_t * 6962306a36Sopenharmony_cihost_to_fcp_swap(uint8_t *fcp, uint32_t bsize) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci uint32_t *ifcp = (uint32_t *) fcp; 7262306a36Sopenharmony_ci uint32_t *ofcp = (uint32_t *) fcp; 7362306a36Sopenharmony_ci uint32_t iter = bsize >> 2; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci for (; iter ; iter--) 7662306a36Sopenharmony_ci *ofcp++ = swab32(*ifcp++); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return fcp; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic inline void 8262306a36Sopenharmony_cihost_to_adap(uint8_t *src, uint8_t *dst, uint32_t bsize) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci uint32_t *isrc = (uint32_t *) src; 8562306a36Sopenharmony_ci __le32 *odest = (__le32 *) dst; 8662306a36Sopenharmony_ci uint32_t iter = bsize >> 2; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci for ( ; iter--; isrc++) 8962306a36Sopenharmony_ci *odest++ = cpu_to_le32(*isrc); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic inline void 9362306a36Sopenharmony_ciqla2x00_clean_dsd_pool(struct qla_hw_data *ha, struct crc_context *ctx) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct dsd_dma *dsd, *tdsd; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* clean up allocated prev pool */ 9862306a36Sopenharmony_ci list_for_each_entry_safe(dsd, tdsd, &ctx->dsd_list, list) { 9962306a36Sopenharmony_ci dma_pool_free(ha->dl_dma_pool, dsd->dsd_addr, 10062306a36Sopenharmony_ci dsd->dsd_list_dma); 10162306a36Sopenharmony_ci list_del(&dsd->list); 10262306a36Sopenharmony_ci kfree(dsd); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->dsd_list); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic inline void 10862306a36Sopenharmony_ciqla2x00_set_fcport_disc_state(fc_port_t *fcport, int state) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int old_val; 11162306a36Sopenharmony_ci uint8_t shiftbits, mask; 11262306a36Sopenharmony_ci uint8_t port_dstate_str_sz; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* This will have to change when the max no. of states > 16 */ 11562306a36Sopenharmony_ci shiftbits = 4; 11662306a36Sopenharmony_ci mask = (1 << shiftbits) - 1; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci port_dstate_str_sz = sizeof(port_dstate_str) / sizeof(char *); 11962306a36Sopenharmony_ci fcport->disc_state = state; 12062306a36Sopenharmony_ci while (1) { 12162306a36Sopenharmony_ci old_val = atomic_read(&fcport->shadow_disc_state); 12262306a36Sopenharmony_ci if (old_val == atomic_cmpxchg(&fcport->shadow_disc_state, 12362306a36Sopenharmony_ci old_val, (old_val << shiftbits) | state)) { 12462306a36Sopenharmony_ci ql_dbg(ql_dbg_disc, fcport->vha, 0x2134, 12562306a36Sopenharmony_ci "FCPort %8phC disc_state transition: %s to %s - portid=%06x.\n", 12662306a36Sopenharmony_ci fcport->port_name, (old_val & mask) < port_dstate_str_sz ? 12762306a36Sopenharmony_ci port_dstate_str[old_val & mask] : "Unknown", 12862306a36Sopenharmony_ci port_dstate_str[state], fcport->d_id.b24); 12962306a36Sopenharmony_ci return; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic inline int 13562306a36Sopenharmony_ciqla2x00_hba_err_chk_enabled(srb_t *sp) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * Uncomment when corresponding SCSI changes are done. 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci if (!sp->cmd->prot_chk) 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci switch (scsi_get_prot_op(GET_CMD_SP(sp))) { 14562306a36Sopenharmony_ci case SCSI_PROT_READ_STRIP: 14662306a36Sopenharmony_ci case SCSI_PROT_WRITE_INSERT: 14762306a36Sopenharmony_ci if (ql2xenablehba_err_chk >= 1) 14862306a36Sopenharmony_ci return 1; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci case SCSI_PROT_READ_PASS: 15162306a36Sopenharmony_ci case SCSI_PROT_WRITE_PASS: 15262306a36Sopenharmony_ci if (ql2xenablehba_err_chk >= 2) 15362306a36Sopenharmony_ci return 1; 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci case SCSI_PROT_READ_INSERT: 15662306a36Sopenharmony_ci case SCSI_PROT_WRITE_STRIP: 15762306a36Sopenharmony_ci return 1; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic inline int 16362306a36Sopenharmony_ciqla2x00_reset_active(scsi_qla_host_t *vha) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci scsi_qla_host_t *base_vha = pci_get_drvdata(vha->hw->pdev); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* Test appropriate base-vha and vha flags. */ 16862306a36Sopenharmony_ci return test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags) || 16962306a36Sopenharmony_ci test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) || 17062306a36Sopenharmony_ci test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) || 17162306a36Sopenharmony_ci test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || 17262306a36Sopenharmony_ci test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic inline int 17662306a36Sopenharmony_ciqla2x00_chip_is_down(scsi_qla_host_t *vha) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci return (qla2x00_reset_active(vha) || !vha->hw->flags.fw_started); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void qla2xxx_init_sp(srb_t *sp, scsi_qla_host_t *vha, 18262306a36Sopenharmony_ci struct qla_qpair *qpair, fc_port_t *fcport) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci memset(sp, 0, sizeof(*sp)); 18562306a36Sopenharmony_ci sp->fcport = fcport; 18662306a36Sopenharmony_ci sp->iocbs = 1; 18762306a36Sopenharmony_ci sp->vha = vha; 18862306a36Sopenharmony_ci sp->qpair = qpair; 18962306a36Sopenharmony_ci sp->cmd_type = TYPE_SRB; 19062306a36Sopenharmony_ci /* ref : INIT - normal flow */ 19162306a36Sopenharmony_ci kref_init(&sp->cmd_kref); 19262306a36Sopenharmony_ci INIT_LIST_HEAD(&sp->elem); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic inline srb_t * 19662306a36Sopenharmony_ciqla2xxx_get_qpair_sp(scsi_qla_host_t *vha, struct qla_qpair *qpair, 19762306a36Sopenharmony_ci fc_port_t *fcport, gfp_t flag) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci srb_t *sp = NULL; 20062306a36Sopenharmony_ci uint8_t bail; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci QLA_QPAIR_MARK_BUSY(qpair, bail); 20362306a36Sopenharmony_ci if (unlikely(bail)) 20462306a36Sopenharmony_ci return NULL; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci sp = mempool_alloc(qpair->srb_mempool, flag); 20762306a36Sopenharmony_ci if (sp) 20862306a36Sopenharmony_ci qla2xxx_init_sp(sp, vha, qpair, fcport); 20962306a36Sopenharmony_ci else 21062306a36Sopenharmony_ci QLA_QPAIR_MARK_NOT_BUSY(qpair); 21162306a36Sopenharmony_ci return sp; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_civoid qla2xxx_rel_done_warning(srb_t *sp, int res); 21562306a36Sopenharmony_civoid qla2xxx_rel_free_warning(srb_t *sp); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic inline void 21862306a36Sopenharmony_ciqla2xxx_rel_qpair_sp(struct qla_qpair *qpair, srb_t *sp) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci sp->qpair = NULL; 22162306a36Sopenharmony_ci sp->done = qla2xxx_rel_done_warning; 22262306a36Sopenharmony_ci sp->free = qla2xxx_rel_free_warning; 22362306a36Sopenharmony_ci mempool_free(sp, qpair->srb_mempool); 22462306a36Sopenharmony_ci QLA_QPAIR_MARK_NOT_BUSY(qpair); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic inline srb_t * 22862306a36Sopenharmony_ciqla2x00_get_sp(scsi_qla_host_t *vha, fc_port_t *fcport, gfp_t flag) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci srb_t *sp = NULL; 23162306a36Sopenharmony_ci struct qla_qpair *qpair; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (unlikely(qla_vha_mark_busy(vha))) 23462306a36Sopenharmony_ci return NULL; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci qpair = vha->hw->base_qpair; 23762306a36Sopenharmony_ci sp = qla2xxx_get_qpair_sp(vha, qpair, fcport, flag); 23862306a36Sopenharmony_ci if (!sp) 23962306a36Sopenharmony_ci goto done; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci sp->vha = vha; 24262306a36Sopenharmony_cidone: 24362306a36Sopenharmony_ci if (!sp) 24462306a36Sopenharmony_ci QLA_VHA_MARK_NOT_BUSY(vha); 24562306a36Sopenharmony_ci return sp; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic inline void 24962306a36Sopenharmony_ciqla2x00_rel_sp(srb_t *sp) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci QLA_VHA_MARK_NOT_BUSY(sp->vha); 25262306a36Sopenharmony_ci qla2xxx_rel_qpair_sp(sp->qpair, sp); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic inline int 25662306a36Sopenharmony_ciqla2x00_gid_list_size(struct qla_hw_data *ha) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci if (IS_QLAFX00(ha)) 25962306a36Sopenharmony_ci return sizeof(uint32_t) * 32; 26062306a36Sopenharmony_ci else 26162306a36Sopenharmony_ci return sizeof(struct gid_list_info) * ha->max_fibre_devices; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic inline void 26562306a36Sopenharmony_ciqla2x00_handle_mbx_completion(struct qla_hw_data *ha, int status) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && 26862306a36Sopenharmony_ci (status & MBX_INTERRUPT) && ha->flags.mbox_int) { 26962306a36Sopenharmony_ci set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); 27062306a36Sopenharmony_ci clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags); 27162306a36Sopenharmony_ci complete(&ha->mbx_intr_comp); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic inline void 27662306a36Sopenharmony_ciqla2x00_set_retry_delay_timestamp(fc_port_t *fcport, uint16_t sts_qual) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci u8 scope; 27962306a36Sopenharmony_ci u16 qual; 28062306a36Sopenharmony_ci#define SQ_SCOPE_MASK 0xc000 /* SAM-6 rev5 5.3.2 */ 28162306a36Sopenharmony_ci#define SQ_SCOPE_SHIFT 14 28262306a36Sopenharmony_ci#define SQ_QUAL_MASK 0x3fff 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci#define SQ_MAX_WAIT_SEC 60 /* Max I/O hold off time in seconds. */ 28562306a36Sopenharmony_ci#define SQ_MAX_WAIT_TIME (SQ_MAX_WAIT_SEC * 10) /* in 100ms. */ 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (!sts_qual) /* Common case. */ 28862306a36Sopenharmony_ci return; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci scope = (sts_qual & SQ_SCOPE_MASK) >> SQ_SCOPE_SHIFT; 29162306a36Sopenharmony_ci /* Handle only scope 1 or 2, which is for I-T nexus. */ 29262306a36Sopenharmony_ci if (scope != 1 && scope != 2) 29362306a36Sopenharmony_ci return; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Skip processing, if retry delay timer is already in effect. */ 29662306a36Sopenharmony_ci if (fcport->retry_delay_timestamp && 29762306a36Sopenharmony_ci time_before(jiffies, fcport->retry_delay_timestamp)) 29862306a36Sopenharmony_ci return; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci qual = sts_qual & SQ_QUAL_MASK; 30162306a36Sopenharmony_ci if (qual < 1 || qual > 0x3fef) 30262306a36Sopenharmony_ci return; 30362306a36Sopenharmony_ci qual = min(qual, (u16)SQ_MAX_WAIT_TIME); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* qual is expressed in 100ms increments. */ 30662306a36Sopenharmony_ci fcport->retry_delay_timestamp = jiffies + (qual * HZ / 10); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ql_log(ql_log_warn, fcport->vha, 0x5101, 30962306a36Sopenharmony_ci "%8phC: I/O throttling requested (status qualifier = %04xh), holding off I/Os for %ums.\n", 31062306a36Sopenharmony_ci fcport->port_name, sts_qual, qual * 100); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic inline bool 31462306a36Sopenharmony_ciqla_is_exch_offld_enabled(struct scsi_qla_host *vha) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci if (qla_ini_mode_enabled(vha) && 31762306a36Sopenharmony_ci (vha->ql2xiniexchg > FW_DEF_EXCHANGES_CNT)) 31862306a36Sopenharmony_ci return true; 31962306a36Sopenharmony_ci else if (qla_tgt_mode_enabled(vha) && 32062306a36Sopenharmony_ci (vha->ql2xexchoffld > FW_DEF_EXCHANGES_CNT)) 32162306a36Sopenharmony_ci return true; 32262306a36Sopenharmony_ci else if (qla_dual_mode_enabled(vha) && 32362306a36Sopenharmony_ci ((vha->ql2xiniexchg + vha->ql2xexchoffld) > FW_DEF_EXCHANGES_CNT)) 32462306a36Sopenharmony_ci return true; 32562306a36Sopenharmony_ci else 32662306a36Sopenharmony_ci return false; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic inline void 33062306a36Sopenharmony_ciqla_cpu_update(struct qla_qpair *qpair, uint16_t cpuid) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci qpair->cpuid = cpuid; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!list_empty(&qpair->hints_list)) { 33562306a36Sopenharmony_ci struct qla_qpair_hint *h; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci list_for_each_entry(h, &qpair->hints_list, hint_elem) 33862306a36Sopenharmony_ci h->cpuid = qpair->cpuid; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic inline struct qla_qpair_hint * 34362306a36Sopenharmony_ciqla_qpair_to_hint(struct qla_tgt *tgt, struct qla_qpair *qpair) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct qla_qpair_hint *h; 34662306a36Sopenharmony_ci u16 i; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci for (i = 0; i < tgt->ha->max_qpairs + 1; i++) { 34962306a36Sopenharmony_ci h = &tgt->qphints[i]; 35062306a36Sopenharmony_ci if (h->qpair == qpair) 35162306a36Sopenharmony_ci return h; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return NULL; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic inline void 35862306a36Sopenharmony_ciqla_83xx_start_iocbs(struct qla_qpair *qpair) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct req_que *req = qpair->req; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci req->ring_index++; 36362306a36Sopenharmony_ci if (req->ring_index == req->length) { 36462306a36Sopenharmony_ci req->ring_index = 0; 36562306a36Sopenharmony_ci req->ring_ptr = req->ring; 36662306a36Sopenharmony_ci } else 36762306a36Sopenharmony_ci req->ring_ptr++; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci wrt_reg_dword(req->req_q_in, req->ring_index); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic inline int 37362306a36Sopenharmony_ciqla2xxx_get_fc4_priority(struct scsi_qla_host *vha) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci uint32_t data; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci data = 37862306a36Sopenharmony_ci ((uint8_t *)vha->hw->nvram)[NVRAM_DUAL_FCP_NVME_FLAG_OFFSET]; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return (data >> 6) & BIT_0 ? FC4_PRIORITY_FCP : FC4_PRIORITY_NVME; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cienum { 38562306a36Sopenharmony_ci RESOURCE_NONE, 38662306a36Sopenharmony_ci RESOURCE_IOCB = BIT_0, 38762306a36Sopenharmony_ci RESOURCE_EXCH = BIT_1, /* exchange */ 38862306a36Sopenharmony_ci RESOURCE_FORCE = BIT_2, 38962306a36Sopenharmony_ci RESOURCE_HA = BIT_3, 39062306a36Sopenharmony_ci}; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic inline int 39362306a36Sopenharmony_ciqla_get_fw_resources(struct qla_qpair *qp, struct iocb_resource *iores) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci u16 iocbs_used, i; 39662306a36Sopenharmony_ci u16 exch_used; 39762306a36Sopenharmony_ci struct qla_hw_data *ha = qp->hw; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (!ql2xenforce_iocb_limit) { 40062306a36Sopenharmony_ci iores->res_type = RESOURCE_NONE; 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci if (iores->res_type & RESOURCE_FORCE) 40462306a36Sopenharmony_ci goto force; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if ((iores->iocb_cnt + qp->fwres.iocbs_used) >= qp->fwres.iocbs_qp_limit) { 40762306a36Sopenharmony_ci /* no need to acquire qpair lock. It's just rough calculation */ 40862306a36Sopenharmony_ci iocbs_used = ha->base_qpair->fwres.iocbs_used; 40962306a36Sopenharmony_ci for (i = 0; i < ha->max_qpairs; i++) { 41062306a36Sopenharmony_ci if (ha->queue_pair_map[i]) 41162306a36Sopenharmony_ci iocbs_used += ha->queue_pair_map[i]->fwres.iocbs_used; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if ((iores->iocb_cnt + iocbs_used) >= qp->fwres.iocbs_limit) { 41562306a36Sopenharmony_ci iores->res_type = RESOURCE_NONE; 41662306a36Sopenharmony_ci return -ENOSPC; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (iores->res_type & RESOURCE_EXCH) { 42162306a36Sopenharmony_ci exch_used = ha->base_qpair->fwres.exch_used; 42262306a36Sopenharmony_ci for (i = 0; i < ha->max_qpairs; i++) { 42362306a36Sopenharmony_ci if (ha->queue_pair_map[i]) 42462306a36Sopenharmony_ci exch_used += ha->queue_pair_map[i]->fwres.exch_used; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if ((exch_used + iores->exch_cnt) >= qp->fwres.exch_limit) { 42862306a36Sopenharmony_ci iores->res_type = RESOURCE_NONE; 42962306a36Sopenharmony_ci return -ENOSPC; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (ql2xenforce_iocb_limit == 2) { 43462306a36Sopenharmony_ci if ((iores->iocb_cnt + atomic_read(&ha->fwres.iocb_used)) >= 43562306a36Sopenharmony_ci ha->fwres.iocb_limit) { 43662306a36Sopenharmony_ci iores->res_type = RESOURCE_NONE; 43762306a36Sopenharmony_ci return -ENOSPC; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (iores->res_type & RESOURCE_EXCH) { 44162306a36Sopenharmony_ci if ((iores->exch_cnt + atomic_read(&ha->fwres.exch_used)) >= 44262306a36Sopenharmony_ci ha->fwres.exch_limit) { 44362306a36Sopenharmony_ci iores->res_type = RESOURCE_NONE; 44462306a36Sopenharmony_ci return -ENOSPC; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ciforce: 45062306a36Sopenharmony_ci qp->fwres.iocbs_used += iores->iocb_cnt; 45162306a36Sopenharmony_ci qp->fwres.exch_used += iores->exch_cnt; 45262306a36Sopenharmony_ci if (ql2xenforce_iocb_limit == 2) { 45362306a36Sopenharmony_ci atomic_add(iores->iocb_cnt, &ha->fwres.iocb_used); 45462306a36Sopenharmony_ci atomic_add(iores->exch_cnt, &ha->fwres.exch_used); 45562306a36Sopenharmony_ci iores->res_type |= RESOURCE_HA; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci/* 46162306a36Sopenharmony_ci * decrement to zero. This routine will not decrement below zero 46262306a36Sopenharmony_ci * @v: pointer of type atomic_t 46362306a36Sopenharmony_ci * @amount: amount to decrement from v 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_cistatic void qla_atomic_dtz(atomic_t *v, int amount) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci int c, old, dec; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci c = atomic_read(v); 47062306a36Sopenharmony_ci for (;;) { 47162306a36Sopenharmony_ci dec = c - amount; 47262306a36Sopenharmony_ci if (unlikely(dec < 0)) 47362306a36Sopenharmony_ci dec = 0; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci old = atomic_cmpxchg((v), c, dec); 47662306a36Sopenharmony_ci if (likely(old == c)) 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci c = old; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic inline void 48362306a36Sopenharmony_ciqla_put_fw_resources(struct qla_qpair *qp, struct iocb_resource *iores) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct qla_hw_data *ha = qp->hw; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (iores->res_type & RESOURCE_HA) { 48862306a36Sopenharmony_ci if (iores->res_type & RESOURCE_IOCB) 48962306a36Sopenharmony_ci qla_atomic_dtz(&ha->fwres.iocb_used, iores->iocb_cnt); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (iores->res_type & RESOURCE_EXCH) 49262306a36Sopenharmony_ci qla_atomic_dtz(&ha->fwres.exch_used, iores->exch_cnt); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (iores->res_type & RESOURCE_IOCB) { 49662306a36Sopenharmony_ci if (qp->fwres.iocbs_used >= iores->iocb_cnt) { 49762306a36Sopenharmony_ci qp->fwres.iocbs_used -= iores->iocb_cnt; 49862306a36Sopenharmony_ci } else { 49962306a36Sopenharmony_ci /* should not happen */ 50062306a36Sopenharmony_ci qp->fwres.iocbs_used = 0; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (iores->res_type & RESOURCE_EXCH) { 50562306a36Sopenharmony_ci if (qp->fwres.exch_used >= iores->exch_cnt) { 50662306a36Sopenharmony_ci qp->fwres.exch_used -= iores->exch_cnt; 50762306a36Sopenharmony_ci } else { 50862306a36Sopenharmony_ci /* should not happen */ 50962306a36Sopenharmony_ci qp->fwres.exch_used = 0; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci iores->res_type = RESOURCE_NONE; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci#define ISP_REG_DISCONNECT 0xffffffffU 51662306a36Sopenharmony_ci/************************************************************************** 51762306a36Sopenharmony_ci * qla2x00_isp_reg_stat 51862306a36Sopenharmony_ci * 51962306a36Sopenharmony_ci * Description: 52062306a36Sopenharmony_ci * Read the host status register of ISP before aborting the command. 52162306a36Sopenharmony_ci * 52262306a36Sopenharmony_ci * Input: 52362306a36Sopenharmony_ci * ha = pointer to host adapter structure. 52462306a36Sopenharmony_ci * 52562306a36Sopenharmony_ci * 52662306a36Sopenharmony_ci * Returns: 52762306a36Sopenharmony_ci * Either true or false. 52862306a36Sopenharmony_ci * 52962306a36Sopenharmony_ci * Note: Return true if there is register disconnect. 53062306a36Sopenharmony_ci **************************************************************************/ 53162306a36Sopenharmony_cistatic inline 53262306a36Sopenharmony_ciuint32_t qla2x00_isp_reg_stat(struct qla_hw_data *ha) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; 53562306a36Sopenharmony_ci struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (IS_P3P_TYPE(ha)) 53862306a36Sopenharmony_ci return ((rd_reg_dword(®82->host_int)) == ISP_REG_DISCONNECT); 53962306a36Sopenharmony_ci else 54062306a36Sopenharmony_ci return ((rd_reg_dword(®->host_status)) == 54162306a36Sopenharmony_ci ISP_REG_DISCONNECT); 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic inline 54562306a36Sopenharmony_cibool qla_pci_disconnected(struct scsi_qla_host *vha, 54662306a36Sopenharmony_ci struct device_reg_24xx __iomem *reg) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci uint32_t stat; 54962306a36Sopenharmony_ci bool ret = false; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci stat = rd_reg_dword(®->host_status); 55262306a36Sopenharmony_ci if (stat == 0xffffffff) { 55362306a36Sopenharmony_ci ql_log(ql_log_info, vha, 0x8041, 55462306a36Sopenharmony_ci "detected PCI disconnect.\n"); 55562306a36Sopenharmony_ci qla_schedule_eeh_work(vha); 55662306a36Sopenharmony_ci ret = true; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci return ret; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic inline bool 56262306a36Sopenharmony_cifcport_is_smaller(fc_port_t *fcport) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci if (wwn_to_u64(fcport->port_name) < 56562306a36Sopenharmony_ci wwn_to_u64(fcport->vha->port_name)) 56662306a36Sopenharmony_ci return true; 56762306a36Sopenharmony_ci else 56862306a36Sopenharmony_ci return false; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic inline bool 57262306a36Sopenharmony_cifcport_is_bigger(fc_port_t *fcport) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci return !fcport_is_smaller(fcport); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic inline struct qla_qpair * 57862306a36Sopenharmony_ciqla_mapq_nvme_select_qpair(struct qla_hw_data *ha, struct qla_qpair *qpair) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci int cpuid = raw_smp_processor_id(); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (qpair->cpuid != cpuid && 58362306a36Sopenharmony_ci ha->qp_cpu_map[cpuid]) { 58462306a36Sopenharmony_ci qpair = ha->qp_cpu_map[cpuid]; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci return qpair; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic inline void 59062306a36Sopenharmony_ciqla_mapq_init_qp_cpu_map(struct qla_hw_data *ha, 59162306a36Sopenharmony_ci struct qla_msix_entry *msix, 59262306a36Sopenharmony_ci struct qla_qpair *qpair) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci const struct cpumask *mask; 59562306a36Sopenharmony_ci unsigned int cpu; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!ha->qp_cpu_map) 59862306a36Sopenharmony_ci return; 59962306a36Sopenharmony_ci mask = pci_irq_get_affinity(ha->pdev, msix->vector_base0); 60062306a36Sopenharmony_ci if (!mask) 60162306a36Sopenharmony_ci return; 60262306a36Sopenharmony_ci qpair->cpuid = cpumask_first(mask); 60362306a36Sopenharmony_ci for_each_cpu(cpu, mask) { 60462306a36Sopenharmony_ci ha->qp_cpu_map[cpu] = qpair; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci msix->cpuid = qpair->cpuid; 60762306a36Sopenharmony_ci qpair->cpu_mapped = true; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic inline void 61162306a36Sopenharmony_ciqla_mapq_free_qp_cpu_map(struct qla_hw_data *ha) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci if (ha->qp_cpu_map) { 61462306a36Sopenharmony_ci kfree(ha->qp_cpu_map); 61562306a36Sopenharmony_ci ha->qp_cpu_map = NULL; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic inline int qla_mapq_alloc_qp_cpu_map(struct qla_hw_data *ha) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (!ha->qp_cpu_map) { 62462306a36Sopenharmony_ci ha->qp_cpu_map = kcalloc(NR_CPUS, sizeof(struct qla_qpair *), 62562306a36Sopenharmony_ci GFP_KERNEL); 62662306a36Sopenharmony_ci if (!ha->qp_cpu_map) { 62762306a36Sopenharmony_ci ql_log(ql_log_fatal, vha, 0x0180, 62862306a36Sopenharmony_ci "Unable to allocate memory for qp_cpu_map ptrs.\n"); 62962306a36Sopenharmony_ci return -1; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci} 634