162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright(c) 2018 - 2020 Intel Corporation. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "hfi.h" 762306a36Sopenharmony_ci#include "affinity.h" 862306a36Sopenharmony_ci#include "sdma.h" 962306a36Sopenharmony_ci#include "netdev.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/** 1262306a36Sopenharmony_ci * msix_initialize() - Calculate, request and configure MSIx IRQs 1362306a36Sopenharmony_ci * @dd: valid hfi1 devdata 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ciint msix_initialize(struct hfi1_devdata *dd) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci u32 total; 1962306a36Sopenharmony_ci int ret; 2062306a36Sopenharmony_ci struct hfi1_msix_entry *entries; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci /* 2362306a36Sopenharmony_ci * MSIx interrupt count: 2462306a36Sopenharmony_ci * one for the general, "slow path" interrupt 2562306a36Sopenharmony_ci * one per used SDMA engine 2662306a36Sopenharmony_ci * one per kernel receive context 2762306a36Sopenharmony_ci * one for each VNIC context 2862306a36Sopenharmony_ci * ...any new IRQs should be added here. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci total = 1 + dd->num_sdma + dd->n_krcv_queues + dd->num_netdev_contexts; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (total >= CCE_NUM_MSIX_VECTORS) 3362306a36Sopenharmony_ci return -EINVAL; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci ret = pci_alloc_irq_vectors(dd->pcidev, total, total, PCI_IRQ_MSIX); 3662306a36Sopenharmony_ci if (ret < 0) { 3762306a36Sopenharmony_ci dd_dev_err(dd, "pci_alloc_irq_vectors() failed: %d\n", ret); 3862306a36Sopenharmony_ci return ret; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci entries = kcalloc(total, sizeof(*dd->msix_info.msix_entries), 4262306a36Sopenharmony_ci GFP_KERNEL); 4362306a36Sopenharmony_ci if (!entries) { 4462306a36Sopenharmony_ci pci_free_irq_vectors(dd->pcidev); 4562306a36Sopenharmony_ci return -ENOMEM; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci dd->msix_info.msix_entries = entries; 4962306a36Sopenharmony_ci spin_lock_init(&dd->msix_info.msix_lock); 5062306a36Sopenharmony_ci bitmap_zero(dd->msix_info.in_use_msix, total); 5162306a36Sopenharmony_ci dd->msix_info.max_requested = total; 5262306a36Sopenharmony_ci dd_dev_info(dd, "%u MSI-X interrupts allocated\n", total); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return 0; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/** 5862306a36Sopenharmony_ci * msix_request_irq() - Allocate a free MSIx IRQ 5962306a36Sopenharmony_ci * @dd: valid devdata 6062306a36Sopenharmony_ci * @arg: context information for the IRQ 6162306a36Sopenharmony_ci * @handler: IRQ handler 6262306a36Sopenharmony_ci * @thread: IRQ thread handler (could be NULL) 6362306a36Sopenharmony_ci * @type: affinty IRQ type 6462306a36Sopenharmony_ci * @name: IRQ name 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * Allocated an MSIx vector if available, and then create the appropriate 6762306a36Sopenharmony_ci * meta data needed to keep track of the pci IRQ request. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * Return: 7062306a36Sopenharmony_ci * < 0 Error 7162306a36Sopenharmony_ci * >= 0 MSIx vector 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistatic int msix_request_irq(struct hfi1_devdata *dd, void *arg, 7562306a36Sopenharmony_ci irq_handler_t handler, irq_handler_t thread, 7662306a36Sopenharmony_ci enum irq_type type, const char *name) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci unsigned long nr; 7962306a36Sopenharmony_ci int irq; 8062306a36Sopenharmony_ci int ret; 8162306a36Sopenharmony_ci struct hfi1_msix_entry *me; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Allocate an MSIx vector */ 8462306a36Sopenharmony_ci spin_lock(&dd->msix_info.msix_lock); 8562306a36Sopenharmony_ci nr = find_first_zero_bit(dd->msix_info.in_use_msix, 8662306a36Sopenharmony_ci dd->msix_info.max_requested); 8762306a36Sopenharmony_ci if (nr < dd->msix_info.max_requested) 8862306a36Sopenharmony_ci __set_bit(nr, dd->msix_info.in_use_msix); 8962306a36Sopenharmony_ci spin_unlock(&dd->msix_info.msix_lock); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (nr == dd->msix_info.max_requested) 9262306a36Sopenharmony_ci return -ENOSPC; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (type < IRQ_SDMA || type >= IRQ_OTHER) 9562306a36Sopenharmony_ci return -EINVAL; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci irq = pci_irq_vector(dd->pcidev, nr); 9862306a36Sopenharmony_ci ret = pci_request_irq(dd->pcidev, nr, handler, thread, arg, name); 9962306a36Sopenharmony_ci if (ret) { 10062306a36Sopenharmony_ci dd_dev_err(dd, 10162306a36Sopenharmony_ci "%s: request for IRQ %d failed, MSIx %lx, err %d\n", 10262306a36Sopenharmony_ci name, irq, nr, ret); 10362306a36Sopenharmony_ci spin_lock(&dd->msix_info.msix_lock); 10462306a36Sopenharmony_ci __clear_bit(nr, dd->msix_info.in_use_msix); 10562306a36Sopenharmony_ci spin_unlock(&dd->msix_info.msix_lock); 10662306a36Sopenharmony_ci return ret; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * assign arg after pci_request_irq call, so it will be 11162306a36Sopenharmony_ci * cleaned up 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci me = &dd->msix_info.msix_entries[nr]; 11462306a36Sopenharmony_ci me->irq = irq; 11562306a36Sopenharmony_ci me->arg = arg; 11662306a36Sopenharmony_ci me->type = type; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* This is a request, so a failure is not fatal */ 11962306a36Sopenharmony_ci ret = hfi1_get_irq_affinity(dd, me); 12062306a36Sopenharmony_ci if (ret) 12162306a36Sopenharmony_ci dd_dev_err(dd, "%s: unable to pin IRQ %d\n", name, ret); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return nr; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int msix_request_rcd_irq_common(struct hfi1_ctxtdata *rcd, 12762306a36Sopenharmony_ci irq_handler_t handler, 12862306a36Sopenharmony_ci irq_handler_t thread, 12962306a36Sopenharmony_ci const char *name) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci int nr = msix_request_irq(rcd->dd, rcd, handler, thread, 13262306a36Sopenharmony_ci rcd->is_vnic ? IRQ_NETDEVCTXT : IRQ_RCVCTXT, 13362306a36Sopenharmony_ci name); 13462306a36Sopenharmony_ci if (nr < 0) 13562306a36Sopenharmony_ci return nr; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * Set the interrupt register and mask for this 13962306a36Sopenharmony_ci * context's interrupt. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci rcd->ireg = (IS_RCVAVAIL_START + rcd->ctxt) / 64; 14262306a36Sopenharmony_ci rcd->imask = ((u64)1) << ((IS_RCVAVAIL_START + rcd->ctxt) % 64); 14362306a36Sopenharmony_ci rcd->msix_intr = nr; 14462306a36Sopenharmony_ci remap_intr(rcd->dd, IS_RCVAVAIL_START + rcd->ctxt, nr); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/** 15062306a36Sopenharmony_ci * msix_request_rcd_irq() - Helper function for RCVAVAIL IRQs 15162306a36Sopenharmony_ci * @rcd: valid rcd context 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ciint msix_request_rcd_irq(struct hfi1_ctxtdata *rcd) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci char name[MAX_NAME_SIZE]; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci snprintf(name, sizeof(name), DRIVER_NAME "_%d kctxt%d", 15962306a36Sopenharmony_ci rcd->dd->unit, rcd->ctxt); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return msix_request_rcd_irq_common(rcd, receive_context_interrupt, 16262306a36Sopenharmony_ci receive_context_thread, name); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/** 16662306a36Sopenharmony_ci * msix_netdev_request_rcd_irq - Helper function for RCVAVAIL IRQs 16762306a36Sopenharmony_ci * for netdev context 16862306a36Sopenharmony_ci * @rcd: valid netdev contexti 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ciint msix_netdev_request_rcd_irq(struct hfi1_ctxtdata *rcd) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci char name[MAX_NAME_SIZE]; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci snprintf(name, sizeof(name), DRIVER_NAME "_%d nd kctxt%d", 17562306a36Sopenharmony_ci rcd->dd->unit, rcd->ctxt); 17662306a36Sopenharmony_ci return msix_request_rcd_irq_common(rcd, receive_context_interrupt_napi, 17762306a36Sopenharmony_ci NULL, name); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/** 18162306a36Sopenharmony_ci * msix_request_sdma_irq - Helper for getting SDMA IRQ resources 18262306a36Sopenharmony_ci * @sde: valid sdma engine 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ciint msix_request_sdma_irq(struct sdma_engine *sde) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci int nr; 18862306a36Sopenharmony_ci char name[MAX_NAME_SIZE]; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci snprintf(name, sizeof(name), DRIVER_NAME "_%d sdma%d", 19162306a36Sopenharmony_ci sde->dd->unit, sde->this_idx); 19262306a36Sopenharmony_ci nr = msix_request_irq(sde->dd, sde, sdma_interrupt, NULL, 19362306a36Sopenharmony_ci IRQ_SDMA, name); 19462306a36Sopenharmony_ci if (nr < 0) 19562306a36Sopenharmony_ci return nr; 19662306a36Sopenharmony_ci sde->msix_intr = nr; 19762306a36Sopenharmony_ci remap_sdma_interrupts(sde->dd, sde->this_idx, nr); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/** 20362306a36Sopenharmony_ci * msix_request_general_irq - Helper for getting general IRQ 20462306a36Sopenharmony_ci * resources 20562306a36Sopenharmony_ci * @dd: valid device data 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ciint msix_request_general_irq(struct hfi1_devdata *dd) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci int nr; 21062306a36Sopenharmony_ci char name[MAX_NAME_SIZE]; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci snprintf(name, sizeof(name), DRIVER_NAME "_%d", dd->unit); 21362306a36Sopenharmony_ci nr = msix_request_irq(dd, dd, general_interrupt, NULL, IRQ_GENERAL, 21462306a36Sopenharmony_ci name); 21562306a36Sopenharmony_ci if (nr < 0) 21662306a36Sopenharmony_ci return nr; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* general interrupt must be MSIx vector 0 */ 21962306a36Sopenharmony_ci if (nr) { 22062306a36Sopenharmony_ci msix_free_irq(dd, (u8)nr); 22162306a36Sopenharmony_ci dd_dev_err(dd, "Invalid index %d for GENERAL IRQ\n", nr); 22262306a36Sopenharmony_ci return -EINVAL; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/** 22962306a36Sopenharmony_ci * enable_sdma_srcs - Helper to enable SDMA IRQ srcs 23062306a36Sopenharmony_ci * @dd: valid devdata structure 23162306a36Sopenharmony_ci * @i: index of SDMA engine 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_cistatic void enable_sdma_srcs(struct hfi1_devdata *dd, int i) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci set_intr_bits(dd, IS_SDMA_START + i, IS_SDMA_START + i, true); 23662306a36Sopenharmony_ci set_intr_bits(dd, IS_SDMA_PROGRESS_START + i, 23762306a36Sopenharmony_ci IS_SDMA_PROGRESS_START + i, true); 23862306a36Sopenharmony_ci set_intr_bits(dd, IS_SDMA_IDLE_START + i, IS_SDMA_IDLE_START + i, true); 23962306a36Sopenharmony_ci set_intr_bits(dd, IS_SDMAENG_ERR_START + i, IS_SDMAENG_ERR_START + i, 24062306a36Sopenharmony_ci true); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/** 24462306a36Sopenharmony_ci * msix_request_irqs() - Allocate all MSIx IRQs 24562306a36Sopenharmony_ci * @dd: valid devdata structure 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * Helper function to request the used MSIx IRQs. 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ciint msix_request_irqs(struct hfi1_devdata *dd) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci int i; 25362306a36Sopenharmony_ci int ret = msix_request_general_irq(dd); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (ret) 25662306a36Sopenharmony_ci return ret; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci for (i = 0; i < dd->num_sdma; i++) { 25962306a36Sopenharmony_ci struct sdma_engine *sde = &dd->per_sdma[i]; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci ret = msix_request_sdma_irq(sde); 26262306a36Sopenharmony_ci if (ret) 26362306a36Sopenharmony_ci return ret; 26462306a36Sopenharmony_ci enable_sdma_srcs(sde->dd, i); 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci for (i = 0; i < dd->n_krcv_queues; i++) { 26862306a36Sopenharmony_ci struct hfi1_ctxtdata *rcd = hfi1_rcd_get_by_index_safe(dd, i); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (rcd) 27162306a36Sopenharmony_ci ret = msix_request_rcd_irq(rcd); 27262306a36Sopenharmony_ci hfi1_rcd_put(rcd); 27362306a36Sopenharmony_ci if (ret) 27462306a36Sopenharmony_ci return ret; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/** 28162306a36Sopenharmony_ci * msix_free_irq() - Free the specified MSIx resources and IRQ 28262306a36Sopenharmony_ci * @dd: valid devdata 28362306a36Sopenharmony_ci * @msix_intr: MSIx vector to free. 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_civoid msix_free_irq(struct hfi1_devdata *dd, u8 msix_intr) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct hfi1_msix_entry *me; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (msix_intr >= dd->msix_info.max_requested) 29162306a36Sopenharmony_ci return; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci me = &dd->msix_info.msix_entries[msix_intr]; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (!me->arg) /* => no irq, no affinity */ 29662306a36Sopenharmony_ci return; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci hfi1_put_irq_affinity(dd, me); 29962306a36Sopenharmony_ci pci_free_irq(dd->pcidev, msix_intr, me->arg); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci me->arg = NULL; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci spin_lock(&dd->msix_info.msix_lock); 30462306a36Sopenharmony_ci __clear_bit(msix_intr, dd->msix_info.in_use_msix); 30562306a36Sopenharmony_ci spin_unlock(&dd->msix_info.msix_lock); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/** 30962306a36Sopenharmony_ci * msix_clean_up_interrupts - Free all MSIx IRQ resources 31062306a36Sopenharmony_ci * @dd: valid device data data structure 31162306a36Sopenharmony_ci * 31262306a36Sopenharmony_ci * Free the MSIx and associated PCI resources, if they have been allocated. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_civoid msix_clean_up_interrupts(struct hfi1_devdata *dd) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci int i; 31762306a36Sopenharmony_ci struct hfi1_msix_entry *me = dd->msix_info.msix_entries; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* remove irqs - must happen before disabling/turning off */ 32062306a36Sopenharmony_ci for (i = 0; i < dd->msix_info.max_requested; i++, me++) 32162306a36Sopenharmony_ci msix_free_irq(dd, i); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* clean structures */ 32462306a36Sopenharmony_ci kfree(dd->msix_info.msix_entries); 32562306a36Sopenharmony_ci dd->msix_info.msix_entries = NULL; 32662306a36Sopenharmony_ci dd->msix_info.max_requested = 0; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci pci_free_irq_vectors(dd->pcidev); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/** 33262306a36Sopenharmony_ci * msix_netdev_synchronize_irq - netdev IRQ synchronize 33362306a36Sopenharmony_ci * @dd: valid devdata 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_civoid msix_netdev_synchronize_irq(struct hfi1_devdata *dd) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci int i; 33862306a36Sopenharmony_ci int ctxt_count = hfi1_netdev_ctxt_count(dd); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci for (i = 0; i < ctxt_count; i++) { 34162306a36Sopenharmony_ci struct hfi1_ctxtdata *rcd = hfi1_netdev_get_ctxt(dd, i); 34262306a36Sopenharmony_ci struct hfi1_msix_entry *me; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci me = &dd->msix_info.msix_entries[rcd->msix_intr]; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci synchronize_irq(me->irq); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci} 349