1// SPDX-License-Identifier: GPL-2.0-only 2// Copyright 2014 Cisco Systems, Inc. All rights reserved. 3 4#include <linux/string.h> 5#include <linux/errno.h> 6#include <linux/pci.h> 7#include <linux/interrupt.h> 8 9#include "vnic_dev.h" 10#include "vnic_intr.h" 11#include "vnic_stats.h" 12#include "snic_io.h" 13#include "snic.h" 14 15 16/* 17 * snic_isr_msix_wq : MSIx ISR for work queue. 18 */ 19 20static irqreturn_t 21snic_isr_msix_wq(int irq, void *data) 22{ 23 struct snic *snic = data; 24 unsigned long wq_work_done = 0; 25 26 snic->s_stats.misc.last_isr_time = jiffies; 27 atomic64_inc(&snic->s_stats.misc.ack_isr_cnt); 28 29 wq_work_done = snic_wq_cmpl_handler(snic, -1); 30 svnic_intr_return_credits(&snic->intr[SNIC_MSIX_WQ], 31 wq_work_done, 32 1 /* unmask intr */, 33 1 /* reset intr timer */); 34 35 return IRQ_HANDLED; 36} /* end of snic_isr_msix_wq */ 37 38static irqreturn_t 39snic_isr_msix_io_cmpl(int irq, void *data) 40{ 41 struct snic *snic = data; 42 unsigned long iocmpl_work_done = 0; 43 44 snic->s_stats.misc.last_isr_time = jiffies; 45 atomic64_inc(&snic->s_stats.misc.cmpl_isr_cnt); 46 47 iocmpl_work_done = snic_fwcq_cmpl_handler(snic, -1); 48 svnic_intr_return_credits(&snic->intr[SNIC_MSIX_IO_CMPL], 49 iocmpl_work_done, 50 1 /* unmask intr */, 51 1 /* reset intr timer */); 52 53 return IRQ_HANDLED; 54} /* end of snic_isr_msix_io_cmpl */ 55 56static irqreturn_t 57snic_isr_msix_err_notify(int irq, void *data) 58{ 59 struct snic *snic = data; 60 61 snic->s_stats.misc.last_isr_time = jiffies; 62 atomic64_inc(&snic->s_stats.misc.errnotify_isr_cnt); 63 64 svnic_intr_return_all_credits(&snic->intr[SNIC_MSIX_ERR_NOTIFY]); 65 snic_log_q_error(snic); 66 67 /*Handling link events */ 68 snic_handle_link_event(snic); 69 70 return IRQ_HANDLED; 71} /* end of snic_isr_msix_err_notify */ 72 73 74void 75snic_free_intr(struct snic *snic) 76{ 77 int i; 78 79 /* ONLY interrupt mode MSIX is supported */ 80 for (i = 0; i < ARRAY_SIZE(snic->msix); i++) { 81 if (snic->msix[i].requested) { 82 free_irq(pci_irq_vector(snic->pdev, i), 83 snic->msix[i].devid); 84 } 85 } 86} /* end of snic_free_intr */ 87 88int 89snic_request_intr(struct snic *snic) 90{ 91 int ret = 0, i; 92 enum vnic_dev_intr_mode intr_mode; 93 94 intr_mode = svnic_dev_get_intr_mode(snic->vdev); 95 SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX); 96 97 /* 98 * Currently HW supports single WQ and CQ. So passing devid as snic. 99 * When hardware supports multiple WQs and CQs, one idea is 100 * to pass devid as corresponding WQ or CQ ptr and retrieve snic 101 * from queue ptr. 102 * Except for err_notify, which is always one. 103 */ 104 sprintf(snic->msix[SNIC_MSIX_WQ].devname, 105 "%.11s-scsi-wq", 106 snic->name); 107 snic->msix[SNIC_MSIX_WQ].isr = snic_isr_msix_wq; 108 snic->msix[SNIC_MSIX_WQ].devid = snic; 109 110 sprintf(snic->msix[SNIC_MSIX_IO_CMPL].devname, 111 "%.11s-io-cmpl", 112 snic->name); 113 snic->msix[SNIC_MSIX_IO_CMPL].isr = snic_isr_msix_io_cmpl; 114 snic->msix[SNIC_MSIX_IO_CMPL].devid = snic; 115 116 sprintf(snic->msix[SNIC_MSIX_ERR_NOTIFY].devname, 117 "%.11s-err-notify", 118 snic->name); 119 snic->msix[SNIC_MSIX_ERR_NOTIFY].isr = snic_isr_msix_err_notify; 120 snic->msix[SNIC_MSIX_ERR_NOTIFY].devid = snic; 121 122 for (i = 0; i < ARRAY_SIZE(snic->msix); i++) { 123 ret = request_irq(pci_irq_vector(snic->pdev, i), 124 snic->msix[i].isr, 125 0, 126 snic->msix[i].devname, 127 snic->msix[i].devid); 128 if (ret) { 129 SNIC_HOST_ERR(snic->shost, 130 "MSI-X: request_irq(%d) failed %d\n", 131 i, 132 ret); 133 snic_free_intr(snic); 134 break; 135 } 136 snic->msix[i].requested = 1; 137 } 138 139 return ret; 140} /* end of snic_request_intr */ 141 142int 143snic_set_intr_mode(struct snic *snic) 144{ 145 unsigned int n = ARRAY_SIZE(snic->wq); 146 unsigned int m = SNIC_CQ_IO_CMPL_MAX; 147 unsigned int vecs = n + m + 1; 148 149 /* 150 * We need n WQs, m CQs, and n+m+1 INTRs 151 * (last INTR is used for WQ/CQ errors and notification area 152 */ 153 BUILD_BUG_ON((ARRAY_SIZE(snic->wq) + SNIC_CQ_IO_CMPL_MAX) > 154 ARRAY_SIZE(snic->intr)); 155 156 if (snic->wq_count < n || snic->cq_count < n + m) 157 goto fail; 158 159 if (pci_alloc_irq_vectors(snic->pdev, vecs, vecs, PCI_IRQ_MSIX) < 0) 160 goto fail; 161 162 snic->wq_count = n; 163 snic->cq_count = n + m; 164 snic->intr_count = vecs; 165 snic->err_intr_offset = SNIC_MSIX_ERR_NOTIFY; 166 167 SNIC_ISR_DBG(snic->shost, "Using MSI-X Interrupts\n"); 168 svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_MSIX); 169 return 0; 170fail: 171 svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN); 172 return -EINVAL; 173} /* end of snic_set_intr_mode */ 174 175void 176snic_clear_intr_mode(struct snic *snic) 177{ 178 pci_free_irq_vectors(snic->pdev); 179 svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_INTX); 180} 181