18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2014 Cisco Systems, Inc. All rights reserved. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you may redistribute it and/or modify 58c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 68c2ecf20Sopenharmony_ci * the Free Software Foundation; version 2 of the License. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 98c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 108c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 118c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 128c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 148c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 158c2ecf20Sopenharmony_ci * SOFTWARE. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/string.h> 198c2ecf20Sopenharmony_ci#include <linux/errno.h> 208c2ecf20Sopenharmony_ci#include <linux/pci.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "vnic_dev.h" 248c2ecf20Sopenharmony_ci#include "vnic_intr.h" 258c2ecf20Sopenharmony_ci#include "vnic_stats.h" 268c2ecf20Sopenharmony_ci#include "snic_io.h" 278c2ecf20Sopenharmony_ci#include "snic.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * snic_isr_msix_wq : MSIx ISR for work queue. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic irqreturn_t 358c2ecf20Sopenharmony_cisnic_isr_msix_wq(int irq, void *data) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct snic *snic = data; 388c2ecf20Sopenharmony_ci unsigned long wq_work_done = 0; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci snic->s_stats.misc.last_isr_time = jiffies; 418c2ecf20Sopenharmony_ci atomic64_inc(&snic->s_stats.misc.ack_isr_cnt); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci wq_work_done = snic_wq_cmpl_handler(snic, -1); 448c2ecf20Sopenharmony_ci svnic_intr_return_credits(&snic->intr[SNIC_MSIX_WQ], 458c2ecf20Sopenharmony_ci wq_work_done, 468c2ecf20Sopenharmony_ci 1 /* unmask intr */, 478c2ecf20Sopenharmony_ci 1 /* reset intr timer */); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return IRQ_HANDLED; 508c2ecf20Sopenharmony_ci} /* end of snic_isr_msix_wq */ 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic irqreturn_t 538c2ecf20Sopenharmony_cisnic_isr_msix_io_cmpl(int irq, void *data) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct snic *snic = data; 568c2ecf20Sopenharmony_ci unsigned long iocmpl_work_done = 0; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci snic->s_stats.misc.last_isr_time = jiffies; 598c2ecf20Sopenharmony_ci atomic64_inc(&snic->s_stats.misc.cmpl_isr_cnt); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci iocmpl_work_done = snic_fwcq_cmpl_handler(snic, -1); 628c2ecf20Sopenharmony_ci svnic_intr_return_credits(&snic->intr[SNIC_MSIX_IO_CMPL], 638c2ecf20Sopenharmony_ci iocmpl_work_done, 648c2ecf20Sopenharmony_ci 1 /* unmask intr */, 658c2ecf20Sopenharmony_ci 1 /* reset intr timer */); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return IRQ_HANDLED; 688c2ecf20Sopenharmony_ci} /* end of snic_isr_msix_io_cmpl */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic irqreturn_t 718c2ecf20Sopenharmony_cisnic_isr_msix_err_notify(int irq, void *data) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct snic *snic = data; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci snic->s_stats.misc.last_isr_time = jiffies; 768c2ecf20Sopenharmony_ci atomic64_inc(&snic->s_stats.misc.errnotify_isr_cnt); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci svnic_intr_return_all_credits(&snic->intr[SNIC_MSIX_ERR_NOTIFY]); 798c2ecf20Sopenharmony_ci snic_log_q_error(snic); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /*Handling link events */ 828c2ecf20Sopenharmony_ci snic_handle_link_event(snic); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return IRQ_HANDLED; 858c2ecf20Sopenharmony_ci} /* end of snic_isr_msix_err_notify */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_civoid 898c2ecf20Sopenharmony_cisnic_free_intr(struct snic *snic) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci int i; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* ONLY interrupt mode MSIX is supported */ 948c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snic->msix); i++) { 958c2ecf20Sopenharmony_ci if (snic->msix[i].requested) { 968c2ecf20Sopenharmony_ci free_irq(pci_irq_vector(snic->pdev, i), 978c2ecf20Sopenharmony_ci snic->msix[i].devid); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci} /* end of snic_free_intr */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ciint 1038c2ecf20Sopenharmony_cisnic_request_intr(struct snic *snic) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci int ret = 0, i; 1068c2ecf20Sopenharmony_ci enum vnic_dev_intr_mode intr_mode; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci intr_mode = svnic_dev_get_intr_mode(snic->vdev); 1098c2ecf20Sopenharmony_ci SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* 1128c2ecf20Sopenharmony_ci * Currently HW supports single WQ and CQ. So passing devid as snic. 1138c2ecf20Sopenharmony_ci * When hardware supports multiple WQs and CQs, one idea is 1148c2ecf20Sopenharmony_ci * to pass devid as corresponding WQ or CQ ptr and retrieve snic 1158c2ecf20Sopenharmony_ci * from queue ptr. 1168c2ecf20Sopenharmony_ci * Except for err_notify, which is always one. 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci sprintf(snic->msix[SNIC_MSIX_WQ].devname, 1198c2ecf20Sopenharmony_ci "%.11s-scsi-wq", 1208c2ecf20Sopenharmony_ci snic->name); 1218c2ecf20Sopenharmony_ci snic->msix[SNIC_MSIX_WQ].isr = snic_isr_msix_wq; 1228c2ecf20Sopenharmony_ci snic->msix[SNIC_MSIX_WQ].devid = snic; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci sprintf(snic->msix[SNIC_MSIX_IO_CMPL].devname, 1258c2ecf20Sopenharmony_ci "%.11s-io-cmpl", 1268c2ecf20Sopenharmony_ci snic->name); 1278c2ecf20Sopenharmony_ci snic->msix[SNIC_MSIX_IO_CMPL].isr = snic_isr_msix_io_cmpl; 1288c2ecf20Sopenharmony_ci snic->msix[SNIC_MSIX_IO_CMPL].devid = snic; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci sprintf(snic->msix[SNIC_MSIX_ERR_NOTIFY].devname, 1318c2ecf20Sopenharmony_ci "%.11s-err-notify", 1328c2ecf20Sopenharmony_ci snic->name); 1338c2ecf20Sopenharmony_ci snic->msix[SNIC_MSIX_ERR_NOTIFY].isr = snic_isr_msix_err_notify; 1348c2ecf20Sopenharmony_ci snic->msix[SNIC_MSIX_ERR_NOTIFY].devid = snic; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snic->msix); i++) { 1378c2ecf20Sopenharmony_ci ret = request_irq(pci_irq_vector(snic->pdev, i), 1388c2ecf20Sopenharmony_ci snic->msix[i].isr, 1398c2ecf20Sopenharmony_ci 0, 1408c2ecf20Sopenharmony_ci snic->msix[i].devname, 1418c2ecf20Sopenharmony_ci snic->msix[i].devid); 1428c2ecf20Sopenharmony_ci if (ret) { 1438c2ecf20Sopenharmony_ci SNIC_HOST_ERR(snic->shost, 1448c2ecf20Sopenharmony_ci "MSI-X: request_irq(%d) failed %d\n", 1458c2ecf20Sopenharmony_ci i, 1468c2ecf20Sopenharmony_ci ret); 1478c2ecf20Sopenharmony_ci snic_free_intr(snic); 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci snic->msix[i].requested = 1; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci} /* end of snic_request_intr */ 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ciint 1578c2ecf20Sopenharmony_cisnic_set_intr_mode(struct snic *snic) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci unsigned int n = ARRAY_SIZE(snic->wq); 1608c2ecf20Sopenharmony_ci unsigned int m = SNIC_CQ_IO_CMPL_MAX; 1618c2ecf20Sopenharmony_ci unsigned int vecs = n + m + 1; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* 1648c2ecf20Sopenharmony_ci * We need n WQs, m CQs, and n+m+1 INTRs 1658c2ecf20Sopenharmony_ci * (last INTR is used for WQ/CQ errors and notification area 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci BUILD_BUG_ON((ARRAY_SIZE(snic->wq) + SNIC_CQ_IO_CMPL_MAX) > 1688c2ecf20Sopenharmony_ci ARRAY_SIZE(snic->intr)); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (snic->wq_count < n || snic->cq_count < n + m) 1718c2ecf20Sopenharmony_ci goto fail; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (pci_alloc_irq_vectors(snic->pdev, vecs, vecs, PCI_IRQ_MSIX) < 0) 1748c2ecf20Sopenharmony_ci goto fail; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci snic->wq_count = n; 1778c2ecf20Sopenharmony_ci snic->cq_count = n + m; 1788c2ecf20Sopenharmony_ci snic->intr_count = vecs; 1798c2ecf20Sopenharmony_ci snic->err_intr_offset = SNIC_MSIX_ERR_NOTIFY; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci SNIC_ISR_DBG(snic->shost, "Using MSI-X Interrupts\n"); 1828c2ecf20Sopenharmony_ci svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_MSIX); 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_cifail: 1858c2ecf20Sopenharmony_ci svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN); 1868c2ecf20Sopenharmony_ci return -EINVAL; 1878c2ecf20Sopenharmony_ci} /* end of snic_set_intr_mode */ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_civoid 1908c2ecf20Sopenharmony_cisnic_clear_intr_mode(struct snic *snic) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci pci_free_irq_vectors(snic->pdev); 1938c2ecf20Sopenharmony_ci svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_INTX); 1948c2ecf20Sopenharmony_ci} 195