18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is part of the Chelsio FCoE driver for Linux. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two 78c2ecf20Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 88c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 98c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the 108c2ecf20Sopenharmony_ci * OpenIB.org BSD license below: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or 138c2ecf20Sopenharmony_ci * without modification, are permitted provided that the following 148c2ecf20Sopenharmony_ci * conditions are met: 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above 178c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 188c2ecf20Sopenharmony_ci * disclaimer. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above 218c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 228c2ecf20Sopenharmony_ci * disclaimer in the documentation and/or other materials 238c2ecf20Sopenharmony_ci * provided with the distribution. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 268c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 278c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 288c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 298c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 308c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 318c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 328c2ecf20Sopenharmony_ci * SOFTWARE. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <linux/kernel.h> 368c2ecf20Sopenharmony_ci#include <linux/pci.h> 378c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 388c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 398c2ecf20Sopenharmony_ci#include <linux/string.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include "csio_init.h" 428c2ecf20Sopenharmony_ci#include "csio_hw.h" 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic irqreturn_t 458c2ecf20Sopenharmony_cicsio_nondata_isr(int irq, void *dev_id) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct csio_hw *hw = (struct csio_hw *) dev_id; 488c2ecf20Sopenharmony_ci int rv; 498c2ecf20Sopenharmony_ci unsigned long flags; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (unlikely(!hw)) 528c2ecf20Sopenharmony_ci return IRQ_NONE; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (unlikely(pci_channel_offline(hw->pdev))) { 558c2ecf20Sopenharmony_ci CSIO_INC_STATS(hw, n_pcich_offline); 568c2ecf20Sopenharmony_ci return IRQ_NONE; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci spin_lock_irqsave(&hw->lock, flags); 608c2ecf20Sopenharmony_ci csio_hw_slow_intr_handler(hw); 618c2ecf20Sopenharmony_ci rv = csio_mb_isr_handler(hw); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (rv == 0 && !(hw->flags & CSIO_HWF_FWEVT_PENDING)) { 648c2ecf20Sopenharmony_ci hw->flags |= CSIO_HWF_FWEVT_PENDING; 658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hw->lock, flags); 668c2ecf20Sopenharmony_ci schedule_work(&hw->evtq_work); 678c2ecf20Sopenharmony_ci return IRQ_HANDLED; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hw->lock, flags); 708c2ecf20Sopenharmony_ci return IRQ_HANDLED; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* 748c2ecf20Sopenharmony_ci * csio_fwevt_handler - Common FW event handler routine. 758c2ecf20Sopenharmony_ci * @hw: HW module. 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * This is the ISR for FW events. It is shared b/w MSIX 788c2ecf20Sopenharmony_ci * and INTx handlers. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistatic void 818c2ecf20Sopenharmony_cicsio_fwevt_handler(struct csio_hw *hw) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci int rv; 848c2ecf20Sopenharmony_ci unsigned long flags; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci rv = csio_fwevtq_handler(hw); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci spin_lock_irqsave(&hw->lock, flags); 898c2ecf20Sopenharmony_ci if (rv == 0 && !(hw->flags & CSIO_HWF_FWEVT_PENDING)) { 908c2ecf20Sopenharmony_ci hw->flags |= CSIO_HWF_FWEVT_PENDING; 918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hw->lock, flags); 928c2ecf20Sopenharmony_ci schedule_work(&hw->evtq_work); 938c2ecf20Sopenharmony_ci return; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hw->lock, flags); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci} /* csio_fwevt_handler */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * csio_fwevt_isr() - FW events MSIX ISR 1018c2ecf20Sopenharmony_ci * @irq: 1028c2ecf20Sopenharmony_ci * @dev_id: 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * Process WRs on the FW event queue. 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_cistatic irqreturn_t 1088c2ecf20Sopenharmony_cicsio_fwevt_isr(int irq, void *dev_id) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct csio_hw *hw = (struct csio_hw *) dev_id; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (unlikely(!hw)) 1138c2ecf20Sopenharmony_ci return IRQ_NONE; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (unlikely(pci_channel_offline(hw->pdev))) { 1168c2ecf20Sopenharmony_ci CSIO_INC_STATS(hw, n_pcich_offline); 1178c2ecf20Sopenharmony_ci return IRQ_NONE; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci csio_fwevt_handler(hw); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* 1268c2ecf20Sopenharmony_ci * csio_fwevt_isr() - INTx wrapper for handling FW events. 1278c2ecf20Sopenharmony_ci * @irq: 1288c2ecf20Sopenharmony_ci * @dev_id: 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_civoid 1318c2ecf20Sopenharmony_cicsio_fwevt_intx_handler(struct csio_hw *hw, void *wr, uint32_t len, 1328c2ecf20Sopenharmony_ci struct csio_fl_dma_buf *flb, void *priv) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci csio_fwevt_handler(hw); 1358c2ecf20Sopenharmony_ci} /* csio_fwevt_intx_handler */ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* 1388c2ecf20Sopenharmony_ci * csio_process_scsi_cmpl - Process a SCSI WR completion. 1398c2ecf20Sopenharmony_ci * @hw: HW module. 1408c2ecf20Sopenharmony_ci * @wr: The completed WR from the ingress queue. 1418c2ecf20Sopenharmony_ci * @len: Length of the WR. 1428c2ecf20Sopenharmony_ci * @flb: Freelist buffer array. 1438c2ecf20Sopenharmony_ci * 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_cistatic void 1468c2ecf20Sopenharmony_cicsio_process_scsi_cmpl(struct csio_hw *hw, void *wr, uint32_t len, 1478c2ecf20Sopenharmony_ci struct csio_fl_dma_buf *flb, void *cbfn_q) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct csio_ioreq *ioreq; 1508c2ecf20Sopenharmony_ci uint8_t *scsiwr; 1518c2ecf20Sopenharmony_ci uint8_t subop; 1528c2ecf20Sopenharmony_ci void *cmnd; 1538c2ecf20Sopenharmony_ci unsigned long flags; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci ioreq = csio_scsi_cmpl_handler(hw, wr, len, flb, NULL, &scsiwr); 1568c2ecf20Sopenharmony_ci if (likely(ioreq)) { 1578c2ecf20Sopenharmony_ci if (unlikely(*scsiwr == FW_SCSI_ABRT_CLS_WR)) { 1588c2ecf20Sopenharmony_ci subop = FW_SCSI_ABRT_CLS_WR_SUB_OPCODE_GET( 1598c2ecf20Sopenharmony_ci ((struct fw_scsi_abrt_cls_wr *) 1608c2ecf20Sopenharmony_ci scsiwr)->sub_opcode_to_chk_all_io); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci csio_dbg(hw, "%s cmpl recvd ioreq:%p status:%d\n", 1638c2ecf20Sopenharmony_ci subop ? "Close" : "Abort", 1648c2ecf20Sopenharmony_ci ioreq, ioreq->wr_status); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci spin_lock_irqsave(&hw->lock, flags); 1678c2ecf20Sopenharmony_ci if (subop) 1688c2ecf20Sopenharmony_ci csio_scsi_closed(ioreq, 1698c2ecf20Sopenharmony_ci (struct list_head *)cbfn_q); 1708c2ecf20Sopenharmony_ci else 1718c2ecf20Sopenharmony_ci csio_scsi_aborted(ioreq, 1728c2ecf20Sopenharmony_ci (struct list_head *)cbfn_q); 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * We call scsi_done for I/Os that driver thinks aborts 1758c2ecf20Sopenharmony_ci * have timed out. If there is a race caused by FW 1768c2ecf20Sopenharmony_ci * completing abort at the exact same time that the 1778c2ecf20Sopenharmony_ci * driver has deteced the abort timeout, the following 1788c2ecf20Sopenharmony_ci * check prevents calling of scsi_done twice for the 1798c2ecf20Sopenharmony_ci * same command: once from the eh_abort_handler, another 1808c2ecf20Sopenharmony_ci * from csio_scsi_isr_handler(). This also avoids the 1818c2ecf20Sopenharmony_ci * need to check if csio_scsi_cmnd(req) is NULL in the 1828c2ecf20Sopenharmony_ci * fast path. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci cmnd = csio_scsi_cmnd(ioreq); 1858c2ecf20Sopenharmony_ci if (unlikely(cmnd == NULL)) 1868c2ecf20Sopenharmony_ci list_del_init(&ioreq->sm.sm_list); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hw->lock, flags); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (unlikely(cmnd == NULL)) 1918c2ecf20Sopenharmony_ci csio_put_scsi_ioreq_lock(hw, 1928c2ecf20Sopenharmony_ci csio_hw_to_scsim(hw), ioreq); 1938c2ecf20Sopenharmony_ci } else { 1948c2ecf20Sopenharmony_ci spin_lock_irqsave(&hw->lock, flags); 1958c2ecf20Sopenharmony_ci csio_scsi_completed(ioreq, (struct list_head *)cbfn_q); 1968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hw->lock, flags); 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* 2028c2ecf20Sopenharmony_ci * csio_scsi_isr_handler() - Common SCSI ISR handler. 2038c2ecf20Sopenharmony_ci * @iq: Ingress queue pointer. 2048c2ecf20Sopenharmony_ci * 2058c2ecf20Sopenharmony_ci * Processes SCSI completions on the SCSI IQ indicated by scm->iq_idx 2068c2ecf20Sopenharmony_ci * by calling csio_wr_process_iq_idx. If there are completions on the 2078c2ecf20Sopenharmony_ci * isr_cbfn_q, yank them out into a local queue and call their io_cbfns. 2088c2ecf20Sopenharmony_ci * Once done, add these completions onto the freelist. 2098c2ecf20Sopenharmony_ci * This routine is shared b/w MSIX and INTx. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_cistatic inline irqreturn_t 2128c2ecf20Sopenharmony_cicsio_scsi_isr_handler(struct csio_q *iq) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct csio_hw *hw = (struct csio_hw *)iq->owner; 2158c2ecf20Sopenharmony_ci LIST_HEAD(cbfn_q); 2168c2ecf20Sopenharmony_ci struct list_head *tmp; 2178c2ecf20Sopenharmony_ci struct csio_scsim *scm; 2188c2ecf20Sopenharmony_ci struct csio_ioreq *ioreq; 2198c2ecf20Sopenharmony_ci int isr_completions = 0; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci scm = csio_hw_to_scsim(hw); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (unlikely(csio_wr_process_iq(hw, iq, csio_process_scsi_cmpl, 2248c2ecf20Sopenharmony_ci &cbfn_q) != 0)) 2258c2ecf20Sopenharmony_ci return IRQ_NONE; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* Call back the completion routines */ 2288c2ecf20Sopenharmony_ci list_for_each(tmp, &cbfn_q) { 2298c2ecf20Sopenharmony_ci ioreq = (struct csio_ioreq *)tmp; 2308c2ecf20Sopenharmony_ci isr_completions++; 2318c2ecf20Sopenharmony_ci ioreq->io_cbfn(hw, ioreq); 2328c2ecf20Sopenharmony_ci /* Release ddp buffer if used for this req */ 2338c2ecf20Sopenharmony_ci if (unlikely(ioreq->dcopy)) 2348c2ecf20Sopenharmony_ci csio_put_scsi_ddp_list_lock(hw, scm, &ioreq->gen_list, 2358c2ecf20Sopenharmony_ci ioreq->nsge); 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (isr_completions) { 2398c2ecf20Sopenharmony_ci /* Return the ioreqs back to ioreq->freelist */ 2408c2ecf20Sopenharmony_ci csio_put_scsi_ioreq_list_lock(hw, scm, &cbfn_q, 2418c2ecf20Sopenharmony_ci isr_completions); 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* 2488c2ecf20Sopenharmony_ci * csio_scsi_isr() - SCSI MSIX handler 2498c2ecf20Sopenharmony_ci * @irq: 2508c2ecf20Sopenharmony_ci * @dev_id: 2518c2ecf20Sopenharmony_ci * 2528c2ecf20Sopenharmony_ci * This is the top level SCSI MSIX handler. Calls csio_scsi_isr_handler() 2538c2ecf20Sopenharmony_ci * for handling SCSI completions. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_cistatic irqreturn_t 2568c2ecf20Sopenharmony_cicsio_scsi_isr(int irq, void *dev_id) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct csio_q *iq = (struct csio_q *) dev_id; 2598c2ecf20Sopenharmony_ci struct csio_hw *hw; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (unlikely(!iq)) 2628c2ecf20Sopenharmony_ci return IRQ_NONE; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci hw = (struct csio_hw *)iq->owner; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (unlikely(pci_channel_offline(hw->pdev))) { 2678c2ecf20Sopenharmony_ci CSIO_INC_STATS(hw, n_pcich_offline); 2688c2ecf20Sopenharmony_ci return IRQ_NONE; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci csio_scsi_isr_handler(iq); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/* 2778c2ecf20Sopenharmony_ci * csio_scsi_intx_handler() - SCSI INTx handler 2788c2ecf20Sopenharmony_ci * @irq: 2798c2ecf20Sopenharmony_ci * @dev_id: 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * This is the top level SCSI INTx handler. Calls csio_scsi_isr_handler() 2828c2ecf20Sopenharmony_ci * for handling SCSI completions. 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_civoid 2858c2ecf20Sopenharmony_cicsio_scsi_intx_handler(struct csio_hw *hw, void *wr, uint32_t len, 2868c2ecf20Sopenharmony_ci struct csio_fl_dma_buf *flb, void *priv) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct csio_q *iq = priv; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci csio_scsi_isr_handler(iq); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci} /* csio_scsi_intx_handler */ 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * csio_fcoe_isr() - INTx/MSI interrupt service routine for FCoE. 2968c2ecf20Sopenharmony_ci * @irq: 2978c2ecf20Sopenharmony_ci * @dev_id: 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_cistatic irqreturn_t 3028c2ecf20Sopenharmony_cicsio_fcoe_isr(int irq, void *dev_id) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct csio_hw *hw = (struct csio_hw *) dev_id; 3058c2ecf20Sopenharmony_ci struct csio_q *intx_q = NULL; 3068c2ecf20Sopenharmony_ci int rv; 3078c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 3088c2ecf20Sopenharmony_ci unsigned long flags; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (unlikely(!hw)) 3118c2ecf20Sopenharmony_ci return IRQ_NONE; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (unlikely(pci_channel_offline(hw->pdev))) { 3148c2ecf20Sopenharmony_ci CSIO_INC_STATS(hw, n_pcich_offline); 3158c2ecf20Sopenharmony_ci return IRQ_NONE; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Disable the interrupt for this PCI function. */ 3198c2ecf20Sopenharmony_ci if (hw->intr_mode == CSIO_IM_INTX) 3208c2ecf20Sopenharmony_ci csio_wr_reg32(hw, 0, MYPF_REG(PCIE_PF_CLI_A)); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* 3238c2ecf20Sopenharmony_ci * The read in the following function will flush the 3248c2ecf20Sopenharmony_ci * above write. 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ci if (csio_hw_slow_intr_handler(hw)) 3278c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Get the INTx Forward interrupt IQ. */ 3308c2ecf20Sopenharmony_ci intx_q = csio_get_q(hw, hw->intr_iq_idx); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci CSIO_DB_ASSERT(intx_q); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* IQ handler is not possible for intx_q, hence pass in NULL */ 3358c2ecf20Sopenharmony_ci if (likely(csio_wr_process_iq(hw, intx_q, NULL, NULL) == 0)) 3368c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci spin_lock_irqsave(&hw->lock, flags); 3398c2ecf20Sopenharmony_ci rv = csio_mb_isr_handler(hw); 3408c2ecf20Sopenharmony_ci if (rv == 0 && !(hw->flags & CSIO_HWF_FWEVT_PENDING)) { 3418c2ecf20Sopenharmony_ci hw->flags |= CSIO_HWF_FWEVT_PENDING; 3428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hw->lock, flags); 3438c2ecf20Sopenharmony_ci schedule_work(&hw->evtq_work); 3448c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hw->lock, flags); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return ret; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic void 3528c2ecf20Sopenharmony_cicsio_add_msix_desc(struct csio_hw *hw) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci int i; 3558c2ecf20Sopenharmony_ci struct csio_msix_entries *entryp = &hw->msix_entries[0]; 3568c2ecf20Sopenharmony_ci int k = CSIO_EXTRA_VECS; 3578c2ecf20Sopenharmony_ci int len = sizeof(entryp->desc) - 1; 3588c2ecf20Sopenharmony_ci int cnt = hw->num_sqsets + k; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* Non-data vector */ 3618c2ecf20Sopenharmony_ci memset(entryp->desc, 0, len + 1); 3628c2ecf20Sopenharmony_ci snprintf(entryp->desc, len, "csio-%02x:%02x:%x-nondata", 3638c2ecf20Sopenharmony_ci CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw), CSIO_PCI_FUNC(hw)); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci entryp++; 3668c2ecf20Sopenharmony_ci memset(entryp->desc, 0, len + 1); 3678c2ecf20Sopenharmony_ci snprintf(entryp->desc, len, "csio-%02x:%02x:%x-fwevt", 3688c2ecf20Sopenharmony_ci CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw), CSIO_PCI_FUNC(hw)); 3698c2ecf20Sopenharmony_ci entryp++; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* Name SCSI vecs */ 3728c2ecf20Sopenharmony_ci for (i = k; i < cnt; i++, entryp++) { 3738c2ecf20Sopenharmony_ci memset(entryp->desc, 0, len + 1); 3748c2ecf20Sopenharmony_ci snprintf(entryp->desc, len, "csio-%02x:%02x:%x-scsi%d", 3758c2ecf20Sopenharmony_ci CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw), 3768c2ecf20Sopenharmony_ci CSIO_PCI_FUNC(hw), i - CSIO_EXTRA_VECS); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ciint 3818c2ecf20Sopenharmony_cicsio_request_irqs(struct csio_hw *hw) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci int rv, i, j, k = 0; 3848c2ecf20Sopenharmony_ci struct csio_msix_entries *entryp = &hw->msix_entries[0]; 3858c2ecf20Sopenharmony_ci struct csio_scsi_cpu_info *info; 3868c2ecf20Sopenharmony_ci struct pci_dev *pdev = hw->pdev; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (hw->intr_mode != CSIO_IM_MSIX) { 3898c2ecf20Sopenharmony_ci rv = request_irq(pci_irq_vector(pdev, 0), csio_fcoe_isr, 3908c2ecf20Sopenharmony_ci hw->intr_mode == CSIO_IM_MSI ? 0 : IRQF_SHARED, 3918c2ecf20Sopenharmony_ci KBUILD_MODNAME, hw); 3928c2ecf20Sopenharmony_ci if (rv) { 3938c2ecf20Sopenharmony_ci csio_err(hw, "Failed to allocate interrupt line.\n"); 3948c2ecf20Sopenharmony_ci goto out_free_irqs; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci goto out; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* Add the MSIX vector descriptions */ 4018c2ecf20Sopenharmony_ci csio_add_msix_desc(hw); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci rv = request_irq(pci_irq_vector(pdev, k), csio_nondata_isr, 0, 4048c2ecf20Sopenharmony_ci entryp[k].desc, hw); 4058c2ecf20Sopenharmony_ci if (rv) { 4068c2ecf20Sopenharmony_ci csio_err(hw, "IRQ request failed for vec %d err:%d\n", 4078c2ecf20Sopenharmony_ci pci_irq_vector(pdev, k), rv); 4088c2ecf20Sopenharmony_ci goto out_free_irqs; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci entryp[k++].dev_id = hw; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci rv = request_irq(pci_irq_vector(pdev, k), csio_fwevt_isr, 0, 4148c2ecf20Sopenharmony_ci entryp[k].desc, hw); 4158c2ecf20Sopenharmony_ci if (rv) { 4168c2ecf20Sopenharmony_ci csio_err(hw, "IRQ request failed for vec %d err:%d\n", 4178c2ecf20Sopenharmony_ci pci_irq_vector(pdev, k), rv); 4188c2ecf20Sopenharmony_ci goto out_free_irqs; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci entryp[k++].dev_id = (void *)hw; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Allocate IRQs for SCSI */ 4248c2ecf20Sopenharmony_ci for (i = 0; i < hw->num_pports; i++) { 4258c2ecf20Sopenharmony_ci info = &hw->scsi_cpu_info[i]; 4268c2ecf20Sopenharmony_ci for (j = 0; j < info->max_cpus; j++, k++) { 4278c2ecf20Sopenharmony_ci struct csio_scsi_qset *sqset = &hw->sqset[i][j]; 4288c2ecf20Sopenharmony_ci struct csio_q *q = hw->wrm.q_arr[sqset->iq_idx]; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci rv = request_irq(pci_irq_vector(pdev, k), csio_scsi_isr, 0, 4318c2ecf20Sopenharmony_ci entryp[k].desc, q); 4328c2ecf20Sopenharmony_ci if (rv) { 4338c2ecf20Sopenharmony_ci csio_err(hw, 4348c2ecf20Sopenharmony_ci "IRQ request failed for vec %d err:%d\n", 4358c2ecf20Sopenharmony_ci pci_irq_vector(pdev, k), rv); 4368c2ecf20Sopenharmony_ci goto out_free_irqs; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci entryp[k].dev_id = q; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci } /* for all scsi cpus */ 4428c2ecf20Sopenharmony_ci } /* for all ports */ 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ciout: 4458c2ecf20Sopenharmony_ci hw->flags |= CSIO_HWF_HOST_INTR_ENABLED; 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ciout_free_irqs: 4498c2ecf20Sopenharmony_ci for (i = 0; i < k; i++) 4508c2ecf20Sopenharmony_ci free_irq(pci_irq_vector(pdev, i), hw->msix_entries[i].dev_id); 4518c2ecf20Sopenharmony_ci pci_free_irq_vectors(hw->pdev); 4528c2ecf20Sopenharmony_ci return -EINVAL; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci/* Reduce per-port max possible CPUs */ 4568c2ecf20Sopenharmony_cistatic void 4578c2ecf20Sopenharmony_cicsio_reduce_sqsets(struct csio_hw *hw, int cnt) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci int i; 4608c2ecf20Sopenharmony_ci struct csio_scsi_cpu_info *info; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci while (cnt < hw->num_sqsets) { 4638c2ecf20Sopenharmony_ci for (i = 0; i < hw->num_pports; i++) { 4648c2ecf20Sopenharmony_ci info = &hw->scsi_cpu_info[i]; 4658c2ecf20Sopenharmony_ci if (info->max_cpus > 1) { 4668c2ecf20Sopenharmony_ci info->max_cpus--; 4678c2ecf20Sopenharmony_ci hw->num_sqsets--; 4688c2ecf20Sopenharmony_ci if (hw->num_sqsets <= cnt) 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci csio_dbg(hw, "Reduced sqsets to %d\n", hw->num_sqsets); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic void csio_calc_sets(struct irq_affinity *affd, unsigned int nvecs) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct csio_hw *hw = affd->priv; 4808c2ecf20Sopenharmony_ci u8 i; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (!nvecs) 4838c2ecf20Sopenharmony_ci return; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (nvecs < hw->num_pports) { 4868c2ecf20Sopenharmony_ci affd->nr_sets = 1; 4878c2ecf20Sopenharmony_ci affd->set_size[0] = nvecs; 4888c2ecf20Sopenharmony_ci return; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci affd->nr_sets = hw->num_pports; 4928c2ecf20Sopenharmony_ci for (i = 0; i < hw->num_pports; i++) 4938c2ecf20Sopenharmony_ci affd->set_size[i] = nvecs / hw->num_pports; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic int 4978c2ecf20Sopenharmony_cicsio_enable_msix(struct csio_hw *hw) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci int i, j, k, n, min, cnt; 5008c2ecf20Sopenharmony_ci int extra = CSIO_EXTRA_VECS; 5018c2ecf20Sopenharmony_ci struct csio_scsi_cpu_info *info; 5028c2ecf20Sopenharmony_ci struct irq_affinity desc = { 5038c2ecf20Sopenharmony_ci .pre_vectors = CSIO_EXTRA_VECS, 5048c2ecf20Sopenharmony_ci .calc_sets = csio_calc_sets, 5058c2ecf20Sopenharmony_ci .priv = hw, 5068c2ecf20Sopenharmony_ci }; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (hw->num_pports > IRQ_AFFINITY_MAX_SETS) 5098c2ecf20Sopenharmony_ci return -ENOSPC; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci min = hw->num_pports + extra; 5128c2ecf20Sopenharmony_ci cnt = hw->num_sqsets + extra; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* Max vectors required based on #niqs configured in fw */ 5158c2ecf20Sopenharmony_ci if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS || !csio_is_hw_master(hw)) 5168c2ecf20Sopenharmony_ci cnt = min_t(uint8_t, hw->cfg_niq, cnt); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci csio_dbg(hw, "FW supp #niq:%d, trying %d msix's\n", hw->cfg_niq, cnt); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci cnt = pci_alloc_irq_vectors_affinity(hw->pdev, min, cnt, 5218c2ecf20Sopenharmony_ci PCI_IRQ_MSIX | PCI_IRQ_AFFINITY, &desc); 5228c2ecf20Sopenharmony_ci if (cnt < 0) 5238c2ecf20Sopenharmony_ci return cnt; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (cnt < (hw->num_sqsets + extra)) { 5268c2ecf20Sopenharmony_ci csio_dbg(hw, "Reducing sqsets to %d\n", cnt - extra); 5278c2ecf20Sopenharmony_ci csio_reduce_sqsets(hw, cnt - extra); 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* Distribute vectors */ 5318c2ecf20Sopenharmony_ci k = 0; 5328c2ecf20Sopenharmony_ci csio_set_nondata_intr_idx(hw, k); 5338c2ecf20Sopenharmony_ci csio_set_mb_intr_idx(csio_hw_to_mbm(hw), k++); 5348c2ecf20Sopenharmony_ci csio_set_fwevt_intr_idx(hw, k++); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci for (i = 0; i < hw->num_pports; i++) { 5378c2ecf20Sopenharmony_ci info = &hw->scsi_cpu_info[i]; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci for (j = 0; j < hw->num_scsi_msix_cpus; j++) { 5408c2ecf20Sopenharmony_ci n = (j % info->max_cpus) + k; 5418c2ecf20Sopenharmony_ci hw->sqset[i][j].intr_idx = n; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci k += info->max_cpus; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_civoid 5518c2ecf20Sopenharmony_cicsio_intr_enable(struct csio_hw *hw) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci hw->intr_mode = CSIO_IM_NONE; 5548c2ecf20Sopenharmony_ci hw->flags &= ~CSIO_HWF_HOST_INTR_ENABLED; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* Try MSIX, then MSI or fall back to INTx */ 5578c2ecf20Sopenharmony_ci if ((csio_msi == 2) && !csio_enable_msix(hw)) 5588c2ecf20Sopenharmony_ci hw->intr_mode = CSIO_IM_MSIX; 5598c2ecf20Sopenharmony_ci else { 5608c2ecf20Sopenharmony_ci /* Max iqs required based on #niqs configured in fw */ 5618c2ecf20Sopenharmony_ci if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS || 5628c2ecf20Sopenharmony_ci !csio_is_hw_master(hw)) { 5638c2ecf20Sopenharmony_ci int extra = CSIO_EXTRA_MSI_IQS; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (hw->cfg_niq < (hw->num_sqsets + extra)) { 5668c2ecf20Sopenharmony_ci csio_dbg(hw, "Reducing sqsets to %d\n", 5678c2ecf20Sopenharmony_ci hw->cfg_niq - extra); 5688c2ecf20Sopenharmony_ci csio_reduce_sqsets(hw, hw->cfg_niq - extra); 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if ((csio_msi == 1) && !pci_enable_msi(hw->pdev)) 5738c2ecf20Sopenharmony_ci hw->intr_mode = CSIO_IM_MSI; 5748c2ecf20Sopenharmony_ci else 5758c2ecf20Sopenharmony_ci hw->intr_mode = CSIO_IM_INTX; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci csio_dbg(hw, "Using %s interrupt mode.\n", 5798c2ecf20Sopenharmony_ci (hw->intr_mode == CSIO_IM_MSIX) ? "MSIX" : 5808c2ecf20Sopenharmony_ci ((hw->intr_mode == CSIO_IM_MSI) ? "MSI" : "INTx")); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_civoid 5848c2ecf20Sopenharmony_cicsio_intr_disable(struct csio_hw *hw, bool free) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci csio_hw_intr_disable(hw); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (free) { 5898c2ecf20Sopenharmony_ci int i; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci switch (hw->intr_mode) { 5928c2ecf20Sopenharmony_ci case CSIO_IM_MSIX: 5938c2ecf20Sopenharmony_ci for (i = 0; i < hw->num_sqsets + CSIO_EXTRA_VECS; i++) { 5948c2ecf20Sopenharmony_ci free_irq(pci_irq_vector(hw->pdev, i), 5958c2ecf20Sopenharmony_ci hw->msix_entries[i].dev_id); 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci case CSIO_IM_MSI: 5998c2ecf20Sopenharmony_ci case CSIO_IM_INTX: 6008c2ecf20Sopenharmony_ci free_irq(pci_irq_vector(hw->pdev, 0), hw); 6018c2ecf20Sopenharmony_ci break; 6028c2ecf20Sopenharmony_ci default: 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci pci_free_irq_vectors(hw->pdev); 6088c2ecf20Sopenharmony_ci hw->intr_mode = CSIO_IM_NONE; 6098c2ecf20Sopenharmony_ci hw->flags &= ~CSIO_HWF_HOST_INTR_ENABLED; 6108c2ecf20Sopenharmony_ci} 611