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 20 static irqreturn_t 21 snic_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 38 static irqreturn_t 39 snic_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 56 static irqreturn_t 57 snic_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 74 void 75 snic_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 88 int 89 snic_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 142 int 143 snic_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; 170 fail: 171 svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN); 172 return -EINVAL; 173 } /* end of snic_set_intr_mode */ 174 175 void 176 snic_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