18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) 28c2ecf20Sopenharmony_ci/* Copyright(c) 2014 - 2020 Intel Corporation */ 38c2ecf20Sopenharmony_ci#include <linux/kernel.h> 48c2ecf20Sopenharmony_ci#include <linux/init.h> 58c2ecf20Sopenharmony_ci#include <linux/types.h> 68c2ecf20Sopenharmony_ci#include <linux/pci.h> 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include "adf_accel_devices.h" 118c2ecf20Sopenharmony_ci#include "adf_common_drv.h" 128c2ecf20Sopenharmony_ci#include "adf_cfg.h" 138c2ecf20Sopenharmony_ci#include "adf_cfg_strings.h" 148c2ecf20Sopenharmony_ci#include "adf_cfg_common.h" 158c2ecf20Sopenharmony_ci#include "adf_transport_access_macros.h" 168c2ecf20Sopenharmony_ci#include "adf_transport_internal.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define ADF_MAX_NUM_VFS 32 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int adf_enable_msix(struct adf_accel_dev *accel_dev) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; 238c2ecf20Sopenharmony_ci struct adf_hw_device_data *hw_data = accel_dev->hw_device; 248c2ecf20Sopenharmony_ci u32 msix_num_entries = 1; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci /* If SR-IOV is disabled, add entries for each bank */ 278c2ecf20Sopenharmony_ci if (!accel_dev->pf.vf_info) { 288c2ecf20Sopenharmony_ci int i; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci msix_num_entries += hw_data->num_banks; 318c2ecf20Sopenharmony_ci for (i = 0; i < msix_num_entries; i++) 328c2ecf20Sopenharmony_ci pci_dev_info->msix_entries.entries[i].entry = i; 338c2ecf20Sopenharmony_ci } else { 348c2ecf20Sopenharmony_ci pci_dev_info->msix_entries.entries[0].entry = 358c2ecf20Sopenharmony_ci hw_data->num_banks; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (pci_enable_msix_exact(pci_dev_info->pci_dev, 398c2ecf20Sopenharmony_ci pci_dev_info->msix_entries.entries, 408c2ecf20Sopenharmony_ci msix_num_entries)) { 418c2ecf20Sopenharmony_ci dev_err(&GET_DEV(accel_dev), "Failed to enable MSI-X IRQ(s)\n"); 428c2ecf20Sopenharmony_ci return -EFAULT; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci return 0; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic void adf_disable_msix(struct adf_accel_pci *pci_dev_info) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci pci_disable_msix(pci_dev_info->pci_dev); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic irqreturn_t adf_msix_isr_bundle(int irq, void *bank_ptr) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct adf_etr_bank_data *bank = bank_ptr; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci WRITE_CSR_INT_FLAG_AND_COL(bank->csr_addr, bank->bank_number, 0); 578c2ecf20Sopenharmony_ci tasklet_hi_schedule(&bank->resp_handler); 588c2ecf20Sopenharmony_ci return IRQ_HANDLED; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic irqreturn_t adf_msix_isr_ae(int irq, void *dev_ptr) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct adf_accel_dev *accel_dev = dev_ptr; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_IOV 668c2ecf20Sopenharmony_ci /* If SR-IOV is enabled (vf_info is non-NULL), check for VF->PF ints */ 678c2ecf20Sopenharmony_ci if (accel_dev->pf.vf_info) { 688c2ecf20Sopenharmony_ci struct adf_hw_device_data *hw_data = accel_dev->hw_device; 698c2ecf20Sopenharmony_ci struct adf_bar *pmisc = 708c2ecf20Sopenharmony_ci &GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)]; 718c2ecf20Sopenharmony_ci void __iomem *pmisc_bar_addr = pmisc->virt_addr; 728c2ecf20Sopenharmony_ci unsigned long vf_mask; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Get the interrupt sources triggered by VFs */ 758c2ecf20Sopenharmony_ci vf_mask = ((ADF_CSR_RD(pmisc_bar_addr, ADF_ERRSOU5) & 768c2ecf20Sopenharmony_ci 0x0000FFFF) << 16) | 778c2ecf20Sopenharmony_ci ((ADF_CSR_RD(pmisc_bar_addr, ADF_ERRSOU3) & 788c2ecf20Sopenharmony_ci 0x01FFFE00) >> 9); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (vf_mask) { 818c2ecf20Sopenharmony_ci struct adf_accel_vf_info *vf_info; 828c2ecf20Sopenharmony_ci bool irq_handled = false; 838c2ecf20Sopenharmony_ci int i; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* Disable VF2PF interrupts for VFs with pending ints */ 868c2ecf20Sopenharmony_ci adf_disable_vf2pf_interrupts(accel_dev, vf_mask); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* 898c2ecf20Sopenharmony_ci * Schedule tasklets to handle VF2PF interrupt BHs 908c2ecf20Sopenharmony_ci * unless the VF is malicious and is attempting to 918c2ecf20Sopenharmony_ci * flood the host OS with VF2PF interrupts. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci for_each_set_bit(i, &vf_mask, ADF_MAX_NUM_VFS) { 948c2ecf20Sopenharmony_ci vf_info = accel_dev->pf.vf_info + i; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (!__ratelimit(&vf_info->vf2pf_ratelimit)) { 978c2ecf20Sopenharmony_ci dev_info(&GET_DEV(accel_dev), 988c2ecf20Sopenharmony_ci "Too many ints from VF%d\n", 998c2ecf20Sopenharmony_ci vf_info->vf_nr + 1); 1008c2ecf20Sopenharmony_ci continue; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* Tasklet will re-enable ints from this VF */ 1048c2ecf20Sopenharmony_ci tasklet_hi_schedule(&vf_info->vf2pf_bh_tasklet); 1058c2ecf20Sopenharmony_ci irq_handled = true; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (irq_handled) 1098c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI_IOV */ 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci dev_dbg(&GET_DEV(accel_dev), "qat_dev%d spurious AE interrupt\n", 1158c2ecf20Sopenharmony_ci accel_dev->accel_id); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return IRQ_NONE; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int adf_request_irqs(struct adf_accel_dev *accel_dev) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; 1238c2ecf20Sopenharmony_ci struct adf_hw_device_data *hw_data = accel_dev->hw_device; 1248c2ecf20Sopenharmony_ci struct msix_entry *msixe = pci_dev_info->msix_entries.entries; 1258c2ecf20Sopenharmony_ci struct adf_etr_data *etr_data = accel_dev->transport; 1268c2ecf20Sopenharmony_ci int ret, i = 0; 1278c2ecf20Sopenharmony_ci char *name; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* Request msix irq for all banks unless SR-IOV enabled */ 1308c2ecf20Sopenharmony_ci if (!accel_dev->pf.vf_info) { 1318c2ecf20Sopenharmony_ci for (i = 0; i < hw_data->num_banks; i++) { 1328c2ecf20Sopenharmony_ci struct adf_etr_bank_data *bank = &etr_data->banks[i]; 1338c2ecf20Sopenharmony_ci unsigned int cpu, cpus = num_online_cpus(); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci name = *(pci_dev_info->msix_entries.names + i); 1368c2ecf20Sopenharmony_ci snprintf(name, ADF_MAX_MSIX_VECTOR_NAME, 1378c2ecf20Sopenharmony_ci "qat%d-bundle%d", accel_dev->accel_id, i); 1388c2ecf20Sopenharmony_ci ret = request_irq(msixe[i].vector, 1398c2ecf20Sopenharmony_ci adf_msix_isr_bundle, 0, name, bank); 1408c2ecf20Sopenharmony_ci if (ret) { 1418c2ecf20Sopenharmony_ci dev_err(&GET_DEV(accel_dev), 1428c2ecf20Sopenharmony_ci "failed to enable irq %d for %s\n", 1438c2ecf20Sopenharmony_ci msixe[i].vector, name); 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci cpu = ((accel_dev->accel_id * hw_data->num_banks) + 1488c2ecf20Sopenharmony_ci i) % cpus; 1498c2ecf20Sopenharmony_ci irq_set_affinity_hint(msixe[i].vector, 1508c2ecf20Sopenharmony_ci get_cpu_mask(cpu)); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* Request msix irq for AE */ 1558c2ecf20Sopenharmony_ci name = *(pci_dev_info->msix_entries.names + i); 1568c2ecf20Sopenharmony_ci snprintf(name, ADF_MAX_MSIX_VECTOR_NAME, 1578c2ecf20Sopenharmony_ci "qat%d-ae-cluster", accel_dev->accel_id); 1588c2ecf20Sopenharmony_ci ret = request_irq(msixe[i].vector, adf_msix_isr_ae, 0, name, accel_dev); 1598c2ecf20Sopenharmony_ci if (ret) { 1608c2ecf20Sopenharmony_ci dev_err(&GET_DEV(accel_dev), 1618c2ecf20Sopenharmony_ci "failed to enable irq %d, for %s\n", 1628c2ecf20Sopenharmony_ci msixe[i].vector, name); 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci return ret; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void adf_free_irqs(struct adf_accel_dev *accel_dev) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; 1718c2ecf20Sopenharmony_ci struct adf_hw_device_data *hw_data = accel_dev->hw_device; 1728c2ecf20Sopenharmony_ci struct msix_entry *msixe = pci_dev_info->msix_entries.entries; 1738c2ecf20Sopenharmony_ci struct adf_etr_data *etr_data = accel_dev->transport; 1748c2ecf20Sopenharmony_ci int i = 0; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (pci_dev_info->msix_entries.num_entries > 1) { 1778c2ecf20Sopenharmony_ci for (i = 0; i < hw_data->num_banks; i++) { 1788c2ecf20Sopenharmony_ci irq_set_affinity_hint(msixe[i].vector, NULL); 1798c2ecf20Sopenharmony_ci free_irq(msixe[i].vector, &etr_data->banks[i]); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci irq_set_affinity_hint(msixe[i].vector, NULL); 1838c2ecf20Sopenharmony_ci free_irq(msixe[i].vector, accel_dev); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int adf_isr_alloc_msix_entry_table(struct adf_accel_dev *accel_dev) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci int i; 1898c2ecf20Sopenharmony_ci char **names; 1908c2ecf20Sopenharmony_ci struct msix_entry *entries; 1918c2ecf20Sopenharmony_ci struct adf_hw_device_data *hw_data = accel_dev->hw_device; 1928c2ecf20Sopenharmony_ci u32 msix_num_entries = 1; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* If SR-IOV is disabled (vf_info is NULL), add entries for each bank */ 1958c2ecf20Sopenharmony_ci if (!accel_dev->pf.vf_info) 1968c2ecf20Sopenharmony_ci msix_num_entries += hw_data->num_banks; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci entries = kcalloc_node(msix_num_entries, sizeof(*entries), 1998c2ecf20Sopenharmony_ci GFP_KERNEL, dev_to_node(&GET_DEV(accel_dev))); 2008c2ecf20Sopenharmony_ci if (!entries) 2018c2ecf20Sopenharmony_ci return -ENOMEM; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci names = kcalloc(msix_num_entries, sizeof(char *), GFP_KERNEL); 2048c2ecf20Sopenharmony_ci if (!names) { 2058c2ecf20Sopenharmony_ci kfree(entries); 2068c2ecf20Sopenharmony_ci return -ENOMEM; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci for (i = 0; i < msix_num_entries; i++) { 2098c2ecf20Sopenharmony_ci *(names + i) = kzalloc(ADF_MAX_MSIX_VECTOR_NAME, GFP_KERNEL); 2108c2ecf20Sopenharmony_ci if (!(*(names + i))) 2118c2ecf20Sopenharmony_ci goto err; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci accel_dev->accel_pci_dev.msix_entries.num_entries = msix_num_entries; 2148c2ecf20Sopenharmony_ci accel_dev->accel_pci_dev.msix_entries.entries = entries; 2158c2ecf20Sopenharmony_ci accel_dev->accel_pci_dev.msix_entries.names = names; 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_cierr: 2188c2ecf20Sopenharmony_ci for (i = 0; i < msix_num_entries; i++) 2198c2ecf20Sopenharmony_ci kfree(*(names + i)); 2208c2ecf20Sopenharmony_ci kfree(entries); 2218c2ecf20Sopenharmony_ci kfree(names); 2228c2ecf20Sopenharmony_ci return -ENOMEM; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void adf_isr_free_msix_entry_table(struct adf_accel_dev *accel_dev) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci char **names = accel_dev->accel_pci_dev.msix_entries.names; 2288c2ecf20Sopenharmony_ci int i; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci kfree(accel_dev->accel_pci_dev.msix_entries.entries); 2318c2ecf20Sopenharmony_ci for (i = 0; i < accel_dev->accel_pci_dev.msix_entries.num_entries; i++) 2328c2ecf20Sopenharmony_ci kfree(*(names + i)); 2338c2ecf20Sopenharmony_ci kfree(names); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int adf_setup_bh(struct adf_accel_dev *accel_dev) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct adf_etr_data *priv_data = accel_dev->transport; 2398c2ecf20Sopenharmony_ci struct adf_hw_device_data *hw_data = accel_dev->hw_device; 2408c2ecf20Sopenharmony_ci int i; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci for (i = 0; i < hw_data->num_banks; i++) 2438c2ecf20Sopenharmony_ci tasklet_init(&priv_data->banks[i].resp_handler, 2448c2ecf20Sopenharmony_ci adf_response_handler, 2458c2ecf20Sopenharmony_ci (unsigned long)&priv_data->banks[i]); 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic void adf_cleanup_bh(struct adf_accel_dev *accel_dev) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct adf_etr_data *priv_data = accel_dev->transport; 2528c2ecf20Sopenharmony_ci struct adf_hw_device_data *hw_data = accel_dev->hw_device; 2538c2ecf20Sopenharmony_ci int i; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci for (i = 0; i < hw_data->num_banks; i++) { 2568c2ecf20Sopenharmony_ci tasklet_disable(&priv_data->banks[i].resp_handler); 2578c2ecf20Sopenharmony_ci tasklet_kill(&priv_data->banks[i].resp_handler); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci/** 2628c2ecf20Sopenharmony_ci * adf_isr_resource_free() - Free IRQ for acceleration device 2638c2ecf20Sopenharmony_ci * @accel_dev: Pointer to acceleration device. 2648c2ecf20Sopenharmony_ci * 2658c2ecf20Sopenharmony_ci * Function frees interrupts for acceleration device. 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_civoid adf_isr_resource_free(struct adf_accel_dev *accel_dev) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci adf_free_irqs(accel_dev); 2708c2ecf20Sopenharmony_ci adf_cleanup_bh(accel_dev); 2718c2ecf20Sopenharmony_ci adf_disable_msix(&accel_dev->accel_pci_dev); 2728c2ecf20Sopenharmony_ci adf_isr_free_msix_entry_table(accel_dev); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(adf_isr_resource_free); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/** 2778c2ecf20Sopenharmony_ci * adf_isr_resource_alloc() - Allocate IRQ for acceleration device 2788c2ecf20Sopenharmony_ci * @accel_dev: Pointer to acceleration device. 2798c2ecf20Sopenharmony_ci * 2808c2ecf20Sopenharmony_ci * Function allocates interrupts for acceleration device. 2818c2ecf20Sopenharmony_ci * 2828c2ecf20Sopenharmony_ci * Return: 0 on success, error code otherwise. 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_ciint adf_isr_resource_alloc(struct adf_accel_dev *accel_dev) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci int ret; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = adf_isr_alloc_msix_entry_table(accel_dev); 2898c2ecf20Sopenharmony_ci if (ret) 2908c2ecf20Sopenharmony_ci goto err_out; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci ret = adf_enable_msix(accel_dev); 2938c2ecf20Sopenharmony_ci if (ret) 2948c2ecf20Sopenharmony_ci goto err_free_msix_table; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci ret = adf_setup_bh(accel_dev); 2978c2ecf20Sopenharmony_ci if (ret) 2988c2ecf20Sopenharmony_ci goto err_disable_msix; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci ret = adf_request_irqs(accel_dev); 3018c2ecf20Sopenharmony_ci if (ret) 3028c2ecf20Sopenharmony_ci goto err_cleanup_bh; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cierr_cleanup_bh: 3078c2ecf20Sopenharmony_ci adf_cleanup_bh(accel_dev); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cierr_disable_msix: 3108c2ecf20Sopenharmony_ci adf_disable_msix(&accel_dev->accel_pci_dev); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cierr_free_msix_table: 3138c2ecf20Sopenharmony_ci adf_isr_free_msix_entry_table(accel_dev); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cierr_out: 3168c2ecf20Sopenharmony_ci return ret; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(adf_isr_resource_alloc); 319