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