162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2019 Intel Corporation. All rights rsvd. */ 362306a36Sopenharmony_ci#include <linux/init.h> 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/module.h> 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci#include <linux/pci.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/workqueue.h> 1262306a36Sopenharmony_ci#include <linux/fs.h> 1362306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/idr.h> 1662306a36Sopenharmony_ci#include <linux/iommu.h> 1762306a36Sopenharmony_ci#include <uapi/linux/idxd.h> 1862306a36Sopenharmony_ci#include <linux/dmaengine.h> 1962306a36Sopenharmony_ci#include "../dmaengine.h" 2062306a36Sopenharmony_ci#include "registers.h" 2162306a36Sopenharmony_ci#include "idxd.h" 2262306a36Sopenharmony_ci#include "perfmon.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciMODULE_VERSION(IDXD_DRIVER_VERSION); 2562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2662306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 2762306a36Sopenharmony_ciMODULE_IMPORT_NS(IDXD); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic bool sva = true; 3062306a36Sopenharmony_cimodule_param(sva, bool, 0644); 3162306a36Sopenharmony_ciMODULE_PARM_DESC(sva, "Toggle SVA support on/off"); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cibool tc_override; 3462306a36Sopenharmony_cimodule_param(tc_override, bool, 0644); 3562306a36Sopenharmony_ciMODULE_PARM_DESC(tc_override, "Override traffic class defaults"); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define DRV_NAME "idxd" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cibool support_enqcmd; 4062306a36Sopenharmony_ciDEFINE_IDA(idxd_ida); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct idxd_driver_data idxd_driver_data[] = { 4362306a36Sopenharmony_ci [IDXD_TYPE_DSA] = { 4462306a36Sopenharmony_ci .name_prefix = "dsa", 4562306a36Sopenharmony_ci .type = IDXD_TYPE_DSA, 4662306a36Sopenharmony_ci .compl_size = sizeof(struct dsa_completion_record), 4762306a36Sopenharmony_ci .align = 32, 4862306a36Sopenharmony_ci .dev_type = &dsa_device_type, 4962306a36Sopenharmony_ci .evl_cr_off = offsetof(struct dsa_evl_entry, cr), 5062306a36Sopenharmony_ci .cr_status_off = offsetof(struct dsa_completion_record, status), 5162306a36Sopenharmony_ci .cr_result_off = offsetof(struct dsa_completion_record, result), 5262306a36Sopenharmony_ci }, 5362306a36Sopenharmony_ci [IDXD_TYPE_IAX] = { 5462306a36Sopenharmony_ci .name_prefix = "iax", 5562306a36Sopenharmony_ci .type = IDXD_TYPE_IAX, 5662306a36Sopenharmony_ci .compl_size = sizeof(struct iax_completion_record), 5762306a36Sopenharmony_ci .align = 64, 5862306a36Sopenharmony_ci .dev_type = &iax_device_type, 5962306a36Sopenharmony_ci .evl_cr_off = offsetof(struct iax_evl_entry, cr), 6062306a36Sopenharmony_ci .cr_status_off = offsetof(struct iax_completion_record, status), 6162306a36Sopenharmony_ci .cr_result_off = offsetof(struct iax_completion_record, error_code), 6262306a36Sopenharmony_ci }, 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic struct pci_device_id idxd_pci_tbl[] = { 6662306a36Sopenharmony_ci /* DSA ver 1.0 platforms */ 6762306a36Sopenharmony_ci { PCI_DEVICE_DATA(INTEL, DSA_SPR0, &idxd_driver_data[IDXD_TYPE_DSA]) }, 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* IAX ver 1.0 platforms */ 7062306a36Sopenharmony_ci { PCI_DEVICE_DATA(INTEL, IAX_SPR0, &idxd_driver_data[IDXD_TYPE_IAX]) }, 7162306a36Sopenharmony_ci { 0, } 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, idxd_pci_tbl); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int idxd_setup_interrupts(struct idxd_device *idxd) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct pci_dev *pdev = idxd->pdev; 7862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 7962306a36Sopenharmony_ci struct idxd_irq_entry *ie; 8062306a36Sopenharmony_ci int i, msixcnt; 8162306a36Sopenharmony_ci int rc = 0; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci msixcnt = pci_msix_vec_count(pdev); 8462306a36Sopenharmony_ci if (msixcnt < 0) { 8562306a36Sopenharmony_ci dev_err(dev, "Not MSI-X interrupt capable.\n"); 8662306a36Sopenharmony_ci return -ENOSPC; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci idxd->irq_cnt = msixcnt; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci rc = pci_alloc_irq_vectors(pdev, msixcnt, msixcnt, PCI_IRQ_MSIX); 9162306a36Sopenharmony_ci if (rc != msixcnt) { 9262306a36Sopenharmony_ci dev_err(dev, "Failed enabling %d MSIX entries: %d\n", msixcnt, rc); 9362306a36Sopenharmony_ci return -ENOSPC; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci dev_dbg(dev, "Enabled %d msix vectors\n", msixcnt); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci ie = idxd_get_ie(idxd, 0); 9962306a36Sopenharmony_ci ie->vector = pci_irq_vector(pdev, 0); 10062306a36Sopenharmony_ci rc = request_threaded_irq(ie->vector, NULL, idxd_misc_thread, 0, "idxd-misc", ie); 10162306a36Sopenharmony_ci if (rc < 0) { 10262306a36Sopenharmony_ci dev_err(dev, "Failed to allocate misc interrupt.\n"); 10362306a36Sopenharmony_ci goto err_misc_irq; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci dev_dbg(dev, "Requested idxd-misc handler on msix vector %d\n", ie->vector); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci for (i = 0; i < idxd->max_wqs; i++) { 10862306a36Sopenharmony_ci int msix_idx = i + 1; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci ie = idxd_get_ie(idxd, msix_idx); 11162306a36Sopenharmony_ci ie->id = msix_idx; 11262306a36Sopenharmony_ci ie->int_handle = INVALID_INT_HANDLE; 11362306a36Sopenharmony_ci ie->pasid = IOMMU_PASID_INVALID; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci spin_lock_init(&ie->list_lock); 11662306a36Sopenharmony_ci init_llist_head(&ie->pending_llist); 11762306a36Sopenharmony_ci INIT_LIST_HEAD(&ie->work_list); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci idxd_unmask_error_interrupts(idxd); 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci err_misc_irq: 12462306a36Sopenharmony_ci idxd_mask_error_interrupts(idxd); 12562306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 12662306a36Sopenharmony_ci dev_err(dev, "No usable interrupts\n"); 12762306a36Sopenharmony_ci return rc; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void idxd_cleanup_interrupts(struct idxd_device *idxd) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct pci_dev *pdev = idxd->pdev; 13362306a36Sopenharmony_ci struct idxd_irq_entry *ie; 13462306a36Sopenharmony_ci int msixcnt; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci msixcnt = pci_msix_vec_count(pdev); 13762306a36Sopenharmony_ci if (msixcnt <= 0) 13862306a36Sopenharmony_ci return; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ie = idxd_get_ie(idxd, 0); 14162306a36Sopenharmony_ci idxd_mask_error_interrupts(idxd); 14262306a36Sopenharmony_ci free_irq(ie->vector, ie); 14362306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int idxd_setup_wqs(struct idxd_device *idxd) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct device *dev = &idxd->pdev->dev; 14962306a36Sopenharmony_ci struct idxd_wq *wq; 15062306a36Sopenharmony_ci struct device *conf_dev; 15162306a36Sopenharmony_ci int i, rc; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci idxd->wqs = kcalloc_node(idxd->max_wqs, sizeof(struct idxd_wq *), 15462306a36Sopenharmony_ci GFP_KERNEL, dev_to_node(dev)); 15562306a36Sopenharmony_ci if (!idxd->wqs) 15662306a36Sopenharmony_ci return -ENOMEM; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci idxd->wq_enable_map = bitmap_zalloc_node(idxd->max_wqs, GFP_KERNEL, dev_to_node(dev)); 15962306a36Sopenharmony_ci if (!idxd->wq_enable_map) { 16062306a36Sopenharmony_ci kfree(idxd->wqs); 16162306a36Sopenharmony_ci return -ENOMEM; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci for (i = 0; i < idxd->max_wqs; i++) { 16562306a36Sopenharmony_ci wq = kzalloc_node(sizeof(*wq), GFP_KERNEL, dev_to_node(dev)); 16662306a36Sopenharmony_ci if (!wq) { 16762306a36Sopenharmony_ci rc = -ENOMEM; 16862306a36Sopenharmony_ci goto err; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci idxd_dev_set_type(&wq->idxd_dev, IDXD_DEV_WQ); 17262306a36Sopenharmony_ci conf_dev = wq_confdev(wq); 17362306a36Sopenharmony_ci wq->id = i; 17462306a36Sopenharmony_ci wq->idxd = idxd; 17562306a36Sopenharmony_ci device_initialize(wq_confdev(wq)); 17662306a36Sopenharmony_ci conf_dev->parent = idxd_confdev(idxd); 17762306a36Sopenharmony_ci conf_dev->bus = &dsa_bus_type; 17862306a36Sopenharmony_ci conf_dev->type = &idxd_wq_device_type; 17962306a36Sopenharmony_ci rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id); 18062306a36Sopenharmony_ci if (rc < 0) { 18162306a36Sopenharmony_ci put_device(conf_dev); 18262306a36Sopenharmony_ci goto err; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci mutex_init(&wq->wq_lock); 18662306a36Sopenharmony_ci init_waitqueue_head(&wq->err_queue); 18762306a36Sopenharmony_ci init_completion(&wq->wq_dead); 18862306a36Sopenharmony_ci init_completion(&wq->wq_resurrect); 18962306a36Sopenharmony_ci wq->max_xfer_bytes = WQ_DEFAULT_MAX_XFER; 19062306a36Sopenharmony_ci idxd_wq_set_max_batch_size(idxd->data->type, wq, WQ_DEFAULT_MAX_BATCH); 19162306a36Sopenharmony_ci wq->enqcmds_retries = IDXD_ENQCMDS_RETRIES; 19262306a36Sopenharmony_ci wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev)); 19362306a36Sopenharmony_ci if (!wq->wqcfg) { 19462306a36Sopenharmony_ci put_device(conf_dev); 19562306a36Sopenharmony_ci rc = -ENOMEM; 19662306a36Sopenharmony_ci goto err; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (idxd->hw.wq_cap.op_config) { 20062306a36Sopenharmony_ci wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL); 20162306a36Sopenharmony_ci if (!wq->opcap_bmap) { 20262306a36Sopenharmony_ci put_device(conf_dev); 20362306a36Sopenharmony_ci rc = -ENOMEM; 20462306a36Sopenharmony_ci goto err; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci bitmap_copy(wq->opcap_bmap, idxd->opcap_bmap, IDXD_MAX_OPCAP_BITS); 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci mutex_init(&wq->uc_lock); 20962306a36Sopenharmony_ci xa_init(&wq->upasid_xa); 21062306a36Sopenharmony_ci idxd->wqs[i] = wq; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci err: 21662306a36Sopenharmony_ci while (--i >= 0) { 21762306a36Sopenharmony_ci wq = idxd->wqs[i]; 21862306a36Sopenharmony_ci conf_dev = wq_confdev(wq); 21962306a36Sopenharmony_ci put_device(conf_dev); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci return rc; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int idxd_setup_engines(struct idxd_device *idxd) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct idxd_engine *engine; 22762306a36Sopenharmony_ci struct device *dev = &idxd->pdev->dev; 22862306a36Sopenharmony_ci struct device *conf_dev; 22962306a36Sopenharmony_ci int i, rc; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci idxd->engines = kcalloc_node(idxd->max_engines, sizeof(struct idxd_engine *), 23262306a36Sopenharmony_ci GFP_KERNEL, dev_to_node(dev)); 23362306a36Sopenharmony_ci if (!idxd->engines) 23462306a36Sopenharmony_ci return -ENOMEM; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci for (i = 0; i < idxd->max_engines; i++) { 23762306a36Sopenharmony_ci engine = kzalloc_node(sizeof(*engine), GFP_KERNEL, dev_to_node(dev)); 23862306a36Sopenharmony_ci if (!engine) { 23962306a36Sopenharmony_ci rc = -ENOMEM; 24062306a36Sopenharmony_ci goto err; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci idxd_dev_set_type(&engine->idxd_dev, IDXD_DEV_ENGINE); 24462306a36Sopenharmony_ci conf_dev = engine_confdev(engine); 24562306a36Sopenharmony_ci engine->id = i; 24662306a36Sopenharmony_ci engine->idxd = idxd; 24762306a36Sopenharmony_ci device_initialize(conf_dev); 24862306a36Sopenharmony_ci conf_dev->parent = idxd_confdev(idxd); 24962306a36Sopenharmony_ci conf_dev->bus = &dsa_bus_type; 25062306a36Sopenharmony_ci conf_dev->type = &idxd_engine_device_type; 25162306a36Sopenharmony_ci rc = dev_set_name(conf_dev, "engine%d.%d", idxd->id, engine->id); 25262306a36Sopenharmony_ci if (rc < 0) { 25362306a36Sopenharmony_ci put_device(conf_dev); 25462306a36Sopenharmony_ci goto err; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci idxd->engines[i] = engine; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci err: 26362306a36Sopenharmony_ci while (--i >= 0) { 26462306a36Sopenharmony_ci engine = idxd->engines[i]; 26562306a36Sopenharmony_ci conf_dev = engine_confdev(engine); 26662306a36Sopenharmony_ci put_device(conf_dev); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci return rc; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int idxd_setup_groups(struct idxd_device *idxd) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct device *dev = &idxd->pdev->dev; 27462306a36Sopenharmony_ci struct device *conf_dev; 27562306a36Sopenharmony_ci struct idxd_group *group; 27662306a36Sopenharmony_ci int i, rc; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci idxd->groups = kcalloc_node(idxd->max_groups, sizeof(struct idxd_group *), 27962306a36Sopenharmony_ci GFP_KERNEL, dev_to_node(dev)); 28062306a36Sopenharmony_ci if (!idxd->groups) 28162306a36Sopenharmony_ci return -ENOMEM; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci for (i = 0; i < idxd->max_groups; i++) { 28462306a36Sopenharmony_ci group = kzalloc_node(sizeof(*group), GFP_KERNEL, dev_to_node(dev)); 28562306a36Sopenharmony_ci if (!group) { 28662306a36Sopenharmony_ci rc = -ENOMEM; 28762306a36Sopenharmony_ci goto err; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci idxd_dev_set_type(&group->idxd_dev, IDXD_DEV_GROUP); 29162306a36Sopenharmony_ci conf_dev = group_confdev(group); 29262306a36Sopenharmony_ci group->id = i; 29362306a36Sopenharmony_ci group->idxd = idxd; 29462306a36Sopenharmony_ci device_initialize(conf_dev); 29562306a36Sopenharmony_ci conf_dev->parent = idxd_confdev(idxd); 29662306a36Sopenharmony_ci conf_dev->bus = &dsa_bus_type; 29762306a36Sopenharmony_ci conf_dev->type = &idxd_group_device_type; 29862306a36Sopenharmony_ci rc = dev_set_name(conf_dev, "group%d.%d", idxd->id, group->id); 29962306a36Sopenharmony_ci if (rc < 0) { 30062306a36Sopenharmony_ci put_device(conf_dev); 30162306a36Sopenharmony_ci goto err; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci idxd->groups[i] = group; 30562306a36Sopenharmony_ci if (idxd->hw.version <= DEVICE_VERSION_2 && !tc_override) { 30662306a36Sopenharmony_ci group->tc_a = 1; 30762306a36Sopenharmony_ci group->tc_b = 1; 30862306a36Sopenharmony_ci } else { 30962306a36Sopenharmony_ci group->tc_a = -1; 31062306a36Sopenharmony_ci group->tc_b = -1; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * The default value is the same as the value of 31462306a36Sopenharmony_ci * total read buffers in GRPCAP. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci group->rdbufs_allowed = idxd->max_rdbufs; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci err: 32262306a36Sopenharmony_ci while (--i >= 0) { 32362306a36Sopenharmony_ci group = idxd->groups[i]; 32462306a36Sopenharmony_ci put_device(group_confdev(group)); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci return rc; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic void idxd_cleanup_internals(struct idxd_device *idxd) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci int i; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci for (i = 0; i < idxd->max_groups; i++) 33462306a36Sopenharmony_ci put_device(group_confdev(idxd->groups[i])); 33562306a36Sopenharmony_ci for (i = 0; i < idxd->max_engines; i++) 33662306a36Sopenharmony_ci put_device(engine_confdev(idxd->engines[i])); 33762306a36Sopenharmony_ci for (i = 0; i < idxd->max_wqs; i++) 33862306a36Sopenharmony_ci put_device(wq_confdev(idxd->wqs[i])); 33962306a36Sopenharmony_ci destroy_workqueue(idxd->wq); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic int idxd_init_evl(struct idxd_device *idxd) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct device *dev = &idxd->pdev->dev; 34562306a36Sopenharmony_ci unsigned int evl_cache_size; 34662306a36Sopenharmony_ci struct idxd_evl *evl; 34762306a36Sopenharmony_ci const char *idxd_name; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (idxd->hw.gen_cap.evl_support == 0) 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci evl = kzalloc_node(sizeof(*evl), GFP_KERNEL, dev_to_node(dev)); 35362306a36Sopenharmony_ci if (!evl) 35462306a36Sopenharmony_ci return -ENOMEM; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci spin_lock_init(&evl->lock); 35762306a36Sopenharmony_ci evl->size = IDXD_EVL_SIZE_MIN; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci idxd_name = dev_name(idxd_confdev(idxd)); 36062306a36Sopenharmony_ci evl_cache_size = sizeof(struct idxd_evl_fault) + evl_ent_size(idxd); 36162306a36Sopenharmony_ci /* 36262306a36Sopenharmony_ci * Since completion record in evl_cache will be copied to user 36362306a36Sopenharmony_ci * when handling completion record page fault, need to create 36462306a36Sopenharmony_ci * the cache suitable for user copy. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci idxd->evl_cache = kmem_cache_create_usercopy(idxd_name, evl_cache_size, 36762306a36Sopenharmony_ci 0, 0, 0, evl_cache_size, 36862306a36Sopenharmony_ci NULL); 36962306a36Sopenharmony_ci if (!idxd->evl_cache) { 37062306a36Sopenharmony_ci kfree(evl); 37162306a36Sopenharmony_ci return -ENOMEM; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci idxd->evl = evl; 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic int idxd_setup_internals(struct idxd_device *idxd) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct device *dev = &idxd->pdev->dev; 38162306a36Sopenharmony_ci int rc, i; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci init_waitqueue_head(&idxd->cmd_waitq); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci rc = idxd_setup_wqs(idxd); 38662306a36Sopenharmony_ci if (rc < 0) 38762306a36Sopenharmony_ci goto err_wqs; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci rc = idxd_setup_engines(idxd); 39062306a36Sopenharmony_ci if (rc < 0) 39162306a36Sopenharmony_ci goto err_engine; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci rc = idxd_setup_groups(idxd); 39462306a36Sopenharmony_ci if (rc < 0) 39562306a36Sopenharmony_ci goto err_group; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci idxd->wq = create_workqueue(dev_name(dev)); 39862306a36Sopenharmony_ci if (!idxd->wq) { 39962306a36Sopenharmony_ci rc = -ENOMEM; 40062306a36Sopenharmony_ci goto err_wkq_create; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci rc = idxd_init_evl(idxd); 40462306a36Sopenharmony_ci if (rc < 0) 40562306a36Sopenharmony_ci goto err_evl; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci err_evl: 41062306a36Sopenharmony_ci destroy_workqueue(idxd->wq); 41162306a36Sopenharmony_ci err_wkq_create: 41262306a36Sopenharmony_ci for (i = 0; i < idxd->max_groups; i++) 41362306a36Sopenharmony_ci put_device(group_confdev(idxd->groups[i])); 41462306a36Sopenharmony_ci err_group: 41562306a36Sopenharmony_ci for (i = 0; i < idxd->max_engines; i++) 41662306a36Sopenharmony_ci put_device(engine_confdev(idxd->engines[i])); 41762306a36Sopenharmony_ci err_engine: 41862306a36Sopenharmony_ci for (i = 0; i < idxd->max_wqs; i++) 41962306a36Sopenharmony_ci put_device(wq_confdev(idxd->wqs[i])); 42062306a36Sopenharmony_ci err_wqs: 42162306a36Sopenharmony_ci return rc; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic void idxd_read_table_offsets(struct idxd_device *idxd) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci union offsets_reg offsets; 42762306a36Sopenharmony_ci struct device *dev = &idxd->pdev->dev; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci offsets.bits[0] = ioread64(idxd->reg_base + IDXD_TABLE_OFFSET); 43062306a36Sopenharmony_ci offsets.bits[1] = ioread64(idxd->reg_base + IDXD_TABLE_OFFSET + sizeof(u64)); 43162306a36Sopenharmony_ci idxd->grpcfg_offset = offsets.grpcfg * IDXD_TABLE_MULT; 43262306a36Sopenharmony_ci dev_dbg(dev, "IDXD Group Config Offset: %#x\n", idxd->grpcfg_offset); 43362306a36Sopenharmony_ci idxd->wqcfg_offset = offsets.wqcfg * IDXD_TABLE_MULT; 43462306a36Sopenharmony_ci dev_dbg(dev, "IDXD Work Queue Config Offset: %#x\n", idxd->wqcfg_offset); 43562306a36Sopenharmony_ci idxd->msix_perm_offset = offsets.msix_perm * IDXD_TABLE_MULT; 43662306a36Sopenharmony_ci dev_dbg(dev, "IDXD MSIX Permission Offset: %#x\n", idxd->msix_perm_offset); 43762306a36Sopenharmony_ci idxd->perfmon_offset = offsets.perfmon * IDXD_TABLE_MULT; 43862306a36Sopenharmony_ci dev_dbg(dev, "IDXD Perfmon Offset: %#x\n", idxd->perfmon_offset); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_civoid multi_u64_to_bmap(unsigned long *bmap, u64 *val, int count) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci int i, j, nr; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci for (i = 0, nr = 0; i < count; i++) { 44662306a36Sopenharmony_ci for (j = 0; j < BITS_PER_LONG_LONG; j++) { 44762306a36Sopenharmony_ci if (val[i] & BIT(j)) 44862306a36Sopenharmony_ci set_bit(nr, bmap); 44962306a36Sopenharmony_ci nr++; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic void idxd_read_caps(struct idxd_device *idxd) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct device *dev = &idxd->pdev->dev; 45762306a36Sopenharmony_ci int i; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* reading generic capabilities */ 46062306a36Sopenharmony_ci idxd->hw.gen_cap.bits = ioread64(idxd->reg_base + IDXD_GENCAP_OFFSET); 46162306a36Sopenharmony_ci dev_dbg(dev, "gen_cap: %#llx\n", idxd->hw.gen_cap.bits); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (idxd->hw.gen_cap.cmd_cap) { 46462306a36Sopenharmony_ci idxd->hw.cmd_cap = ioread32(idxd->reg_base + IDXD_CMDCAP_OFFSET); 46562306a36Sopenharmony_ci dev_dbg(dev, "cmd_cap: %#x\n", idxd->hw.cmd_cap); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* reading command capabilities */ 46962306a36Sopenharmony_ci if (idxd->hw.cmd_cap & BIT(IDXD_CMD_REQUEST_INT_HANDLE)) 47062306a36Sopenharmony_ci idxd->request_int_handles = true; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci idxd->max_xfer_bytes = 1ULL << idxd->hw.gen_cap.max_xfer_shift; 47362306a36Sopenharmony_ci dev_dbg(dev, "max xfer size: %llu bytes\n", idxd->max_xfer_bytes); 47462306a36Sopenharmony_ci idxd_set_max_batch_size(idxd->data->type, idxd, 1U << idxd->hw.gen_cap.max_batch_shift); 47562306a36Sopenharmony_ci dev_dbg(dev, "max batch size: %u\n", idxd->max_batch_size); 47662306a36Sopenharmony_ci if (idxd->hw.gen_cap.config_en) 47762306a36Sopenharmony_ci set_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* reading group capabilities */ 48062306a36Sopenharmony_ci idxd->hw.group_cap.bits = 48162306a36Sopenharmony_ci ioread64(idxd->reg_base + IDXD_GRPCAP_OFFSET); 48262306a36Sopenharmony_ci dev_dbg(dev, "group_cap: %#llx\n", idxd->hw.group_cap.bits); 48362306a36Sopenharmony_ci idxd->max_groups = idxd->hw.group_cap.num_groups; 48462306a36Sopenharmony_ci dev_dbg(dev, "max groups: %u\n", idxd->max_groups); 48562306a36Sopenharmony_ci idxd->max_rdbufs = idxd->hw.group_cap.total_rdbufs; 48662306a36Sopenharmony_ci dev_dbg(dev, "max read buffers: %u\n", idxd->max_rdbufs); 48762306a36Sopenharmony_ci idxd->nr_rdbufs = idxd->max_rdbufs; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* read engine capabilities */ 49062306a36Sopenharmony_ci idxd->hw.engine_cap.bits = 49162306a36Sopenharmony_ci ioread64(idxd->reg_base + IDXD_ENGCAP_OFFSET); 49262306a36Sopenharmony_ci dev_dbg(dev, "engine_cap: %#llx\n", idxd->hw.engine_cap.bits); 49362306a36Sopenharmony_ci idxd->max_engines = idxd->hw.engine_cap.num_engines; 49462306a36Sopenharmony_ci dev_dbg(dev, "max engines: %u\n", idxd->max_engines); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* read workqueue capabilities */ 49762306a36Sopenharmony_ci idxd->hw.wq_cap.bits = ioread64(idxd->reg_base + IDXD_WQCAP_OFFSET); 49862306a36Sopenharmony_ci dev_dbg(dev, "wq_cap: %#llx\n", idxd->hw.wq_cap.bits); 49962306a36Sopenharmony_ci idxd->max_wq_size = idxd->hw.wq_cap.total_wq_size; 50062306a36Sopenharmony_ci dev_dbg(dev, "total workqueue size: %u\n", idxd->max_wq_size); 50162306a36Sopenharmony_ci idxd->max_wqs = idxd->hw.wq_cap.num_wqs; 50262306a36Sopenharmony_ci dev_dbg(dev, "max workqueues: %u\n", idxd->max_wqs); 50362306a36Sopenharmony_ci idxd->wqcfg_size = 1 << (idxd->hw.wq_cap.wqcfg_size + IDXD_WQCFG_MIN); 50462306a36Sopenharmony_ci dev_dbg(dev, "wqcfg size: %u\n", idxd->wqcfg_size); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* reading operation capabilities */ 50762306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 50862306a36Sopenharmony_ci idxd->hw.opcap.bits[i] = ioread64(idxd->reg_base + 50962306a36Sopenharmony_ci IDXD_OPCAP_OFFSET + i * sizeof(u64)); 51062306a36Sopenharmony_ci dev_dbg(dev, "opcap[%d]: %#llx\n", i, idxd->hw.opcap.bits[i]); 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci multi_u64_to_bmap(idxd->opcap_bmap, &idxd->hw.opcap.bits[0], 4); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* read iaa cap */ 51562306a36Sopenharmony_ci if (idxd->data->type == IDXD_TYPE_IAX && idxd->hw.version >= DEVICE_VERSION_2) 51662306a36Sopenharmony_ci idxd->hw.iaa_cap.bits = ioread64(idxd->reg_base + IDXD_IAACAP_OFFSET); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic struct idxd_device *idxd_alloc(struct pci_dev *pdev, struct idxd_driver_data *data) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 52262306a36Sopenharmony_ci struct device *conf_dev; 52362306a36Sopenharmony_ci struct idxd_device *idxd; 52462306a36Sopenharmony_ci int rc; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci idxd = kzalloc_node(sizeof(*idxd), GFP_KERNEL, dev_to_node(dev)); 52762306a36Sopenharmony_ci if (!idxd) 52862306a36Sopenharmony_ci return NULL; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci conf_dev = idxd_confdev(idxd); 53162306a36Sopenharmony_ci idxd->pdev = pdev; 53262306a36Sopenharmony_ci idxd->data = data; 53362306a36Sopenharmony_ci idxd_dev_set_type(&idxd->idxd_dev, idxd->data->type); 53462306a36Sopenharmony_ci idxd->id = ida_alloc(&idxd_ida, GFP_KERNEL); 53562306a36Sopenharmony_ci if (idxd->id < 0) 53662306a36Sopenharmony_ci return NULL; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci idxd->opcap_bmap = bitmap_zalloc_node(IDXD_MAX_OPCAP_BITS, GFP_KERNEL, dev_to_node(dev)); 53962306a36Sopenharmony_ci if (!idxd->opcap_bmap) { 54062306a36Sopenharmony_ci ida_free(&idxd_ida, idxd->id); 54162306a36Sopenharmony_ci return NULL; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci device_initialize(conf_dev); 54562306a36Sopenharmony_ci conf_dev->parent = dev; 54662306a36Sopenharmony_ci conf_dev->bus = &dsa_bus_type; 54762306a36Sopenharmony_ci conf_dev->type = idxd->data->dev_type; 54862306a36Sopenharmony_ci rc = dev_set_name(conf_dev, "%s%d", idxd->data->name_prefix, idxd->id); 54962306a36Sopenharmony_ci if (rc < 0) { 55062306a36Sopenharmony_ci put_device(conf_dev); 55162306a36Sopenharmony_ci return NULL; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci spin_lock_init(&idxd->dev_lock); 55562306a36Sopenharmony_ci spin_lock_init(&idxd->cmd_lock); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return idxd; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic int idxd_enable_system_pasid(struct idxd_device *idxd) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct pci_dev *pdev = idxd->pdev; 56362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 56462306a36Sopenharmony_ci struct iommu_domain *domain; 56562306a36Sopenharmony_ci ioasid_t pasid; 56662306a36Sopenharmony_ci int ret; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* 56962306a36Sopenharmony_ci * Attach a global PASID to the DMA domain so that we can use ENQCMDS 57062306a36Sopenharmony_ci * to submit work on buffers mapped by DMA API. 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_ci domain = iommu_get_domain_for_dev(dev); 57362306a36Sopenharmony_ci if (!domain) 57462306a36Sopenharmony_ci return -EPERM; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci pasid = iommu_alloc_global_pasid(dev); 57762306a36Sopenharmony_ci if (pasid == IOMMU_PASID_INVALID) 57862306a36Sopenharmony_ci return -ENOSPC; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci * DMA domain is owned by the driver, it should support all valid 58262306a36Sopenharmony_ci * types such as DMA-FQ, identity, etc. 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_ci ret = iommu_attach_device_pasid(domain, dev, pasid); 58562306a36Sopenharmony_ci if (ret) { 58662306a36Sopenharmony_ci dev_err(dev, "failed to attach device pasid %d, domain type %d", 58762306a36Sopenharmony_ci pasid, domain->type); 58862306a36Sopenharmony_ci iommu_free_global_pasid(pasid); 58962306a36Sopenharmony_ci return ret; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Since we set user privilege for kernel DMA, enable completion IRQ */ 59362306a36Sopenharmony_ci idxd_set_user_intr(idxd, 1); 59462306a36Sopenharmony_ci idxd->pasid = pasid; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return ret; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic void idxd_disable_system_pasid(struct idxd_device *idxd) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci struct pci_dev *pdev = idxd->pdev; 60262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 60362306a36Sopenharmony_ci struct iommu_domain *domain; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci domain = iommu_get_domain_for_dev(dev); 60662306a36Sopenharmony_ci if (!domain) 60762306a36Sopenharmony_ci return; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci iommu_detach_device_pasid(domain, dev, idxd->pasid); 61062306a36Sopenharmony_ci iommu_free_global_pasid(idxd->pasid); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci idxd_set_user_intr(idxd, 0); 61362306a36Sopenharmony_ci idxd->sva = NULL; 61462306a36Sopenharmony_ci idxd->pasid = IOMMU_PASID_INVALID; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic int idxd_enable_sva(struct pci_dev *pdev) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci int ret; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci ret = iommu_dev_enable_feature(&pdev->dev, IOMMU_DEV_FEAT_IOPF); 62262306a36Sopenharmony_ci if (ret) 62362306a36Sopenharmony_ci return ret; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci ret = iommu_dev_enable_feature(&pdev->dev, IOMMU_DEV_FEAT_SVA); 62662306a36Sopenharmony_ci if (ret) 62762306a36Sopenharmony_ci iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_IOPF); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return ret; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic void idxd_disable_sva(struct pci_dev *pdev) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_SVA); 63562306a36Sopenharmony_ci iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_IOPF); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic int idxd_probe(struct idxd_device *idxd) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci struct pci_dev *pdev = idxd->pdev; 64162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 64262306a36Sopenharmony_ci int rc; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci dev_dbg(dev, "%s entered and resetting device\n", __func__); 64562306a36Sopenharmony_ci rc = idxd_device_init_reset(idxd); 64662306a36Sopenharmony_ci if (rc < 0) 64762306a36Sopenharmony_ci return rc; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci dev_dbg(dev, "IDXD reset complete\n"); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_INTEL_IDXD_SVM) && sva) { 65262306a36Sopenharmony_ci if (idxd_enable_sva(pdev)) { 65362306a36Sopenharmony_ci dev_warn(dev, "Unable to turn on user SVA feature.\n"); 65462306a36Sopenharmony_ci } else { 65562306a36Sopenharmony_ci set_bit(IDXD_FLAG_USER_PASID_ENABLED, &idxd->flags); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci rc = idxd_enable_system_pasid(idxd); 65862306a36Sopenharmony_ci if (rc) 65962306a36Sopenharmony_ci dev_warn(dev, "No in-kernel DMA with PASID. %d\n", rc); 66062306a36Sopenharmony_ci else 66162306a36Sopenharmony_ci set_bit(IDXD_FLAG_PASID_ENABLED, &idxd->flags); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci } else if (!sva) { 66462306a36Sopenharmony_ci dev_warn(dev, "User forced SVA off via module param.\n"); 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci idxd_read_caps(idxd); 66862306a36Sopenharmony_ci idxd_read_table_offsets(idxd); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci rc = idxd_setup_internals(idxd); 67162306a36Sopenharmony_ci if (rc) 67262306a36Sopenharmony_ci goto err; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* If the configs are readonly, then load them from device */ 67562306a36Sopenharmony_ci if (!test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags)) { 67662306a36Sopenharmony_ci dev_dbg(dev, "Loading RO device config\n"); 67762306a36Sopenharmony_ci rc = idxd_device_load_config(idxd); 67862306a36Sopenharmony_ci if (rc < 0) 67962306a36Sopenharmony_ci goto err_config; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci rc = idxd_setup_interrupts(idxd); 68362306a36Sopenharmony_ci if (rc) 68462306a36Sopenharmony_ci goto err_config; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci idxd->major = idxd_cdev_get_major(idxd); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci rc = perfmon_pmu_init(idxd); 68962306a36Sopenharmony_ci if (rc < 0) 69062306a36Sopenharmony_ci dev_warn(dev, "Failed to initialize perfmon. No PMU support: %d\n", rc); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci dev_dbg(dev, "IDXD device %d probed successfully\n", idxd->id); 69362306a36Sopenharmony_ci return 0; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci err_config: 69662306a36Sopenharmony_ci idxd_cleanup_internals(idxd); 69762306a36Sopenharmony_ci err: 69862306a36Sopenharmony_ci if (device_pasid_enabled(idxd)) 69962306a36Sopenharmony_ci idxd_disable_system_pasid(idxd); 70062306a36Sopenharmony_ci if (device_user_pasid_enabled(idxd)) 70162306a36Sopenharmony_ci idxd_disable_sva(pdev); 70262306a36Sopenharmony_ci return rc; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic void idxd_cleanup(struct idxd_device *idxd) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci perfmon_pmu_remove(idxd); 70862306a36Sopenharmony_ci idxd_cleanup_interrupts(idxd); 70962306a36Sopenharmony_ci idxd_cleanup_internals(idxd); 71062306a36Sopenharmony_ci if (device_pasid_enabled(idxd)) 71162306a36Sopenharmony_ci idxd_disable_system_pasid(idxd); 71262306a36Sopenharmony_ci if (device_user_pasid_enabled(idxd)) 71362306a36Sopenharmony_ci idxd_disable_sva(idxd->pdev); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic int idxd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 71962306a36Sopenharmony_ci struct idxd_device *idxd; 72062306a36Sopenharmony_ci struct idxd_driver_data *data = (struct idxd_driver_data *)id->driver_data; 72162306a36Sopenharmony_ci int rc; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci rc = pci_enable_device(pdev); 72462306a36Sopenharmony_ci if (rc) 72562306a36Sopenharmony_ci return rc; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci dev_dbg(dev, "Alloc IDXD context\n"); 72862306a36Sopenharmony_ci idxd = idxd_alloc(pdev, data); 72962306a36Sopenharmony_ci if (!idxd) { 73062306a36Sopenharmony_ci rc = -ENOMEM; 73162306a36Sopenharmony_ci goto err_idxd_alloc; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci dev_dbg(dev, "Mapping BARs\n"); 73562306a36Sopenharmony_ci idxd->reg_base = pci_iomap(pdev, IDXD_MMIO_BAR, 0); 73662306a36Sopenharmony_ci if (!idxd->reg_base) { 73762306a36Sopenharmony_ci rc = -ENOMEM; 73862306a36Sopenharmony_ci goto err_iomap; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci dev_dbg(dev, "Set DMA masks\n"); 74262306a36Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 74362306a36Sopenharmony_ci if (rc) 74462306a36Sopenharmony_ci goto err; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci dev_dbg(dev, "Set PCI master\n"); 74762306a36Sopenharmony_ci pci_set_master(pdev); 74862306a36Sopenharmony_ci pci_set_drvdata(pdev, idxd); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci idxd->hw.version = ioread32(idxd->reg_base + IDXD_VER_OFFSET); 75162306a36Sopenharmony_ci rc = idxd_probe(idxd); 75262306a36Sopenharmony_ci if (rc) { 75362306a36Sopenharmony_ci dev_err(dev, "Intel(R) IDXD DMA Engine init failed\n"); 75462306a36Sopenharmony_ci goto err; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci rc = idxd_register_devices(idxd); 75862306a36Sopenharmony_ci if (rc) { 75962306a36Sopenharmony_ci dev_err(dev, "IDXD sysfs setup failed\n"); 76062306a36Sopenharmony_ci goto err_dev_register; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci rc = idxd_device_init_debugfs(idxd); 76462306a36Sopenharmony_ci if (rc) 76562306a36Sopenharmony_ci dev_warn(dev, "IDXD debugfs failed to setup\n"); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci dev_info(&pdev->dev, "Intel(R) Accelerator Device (v%x)\n", 76862306a36Sopenharmony_ci idxd->hw.version); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return 0; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci err_dev_register: 77362306a36Sopenharmony_ci idxd_cleanup(idxd); 77462306a36Sopenharmony_ci err: 77562306a36Sopenharmony_ci pci_iounmap(pdev, idxd->reg_base); 77662306a36Sopenharmony_ci err_iomap: 77762306a36Sopenharmony_ci put_device(idxd_confdev(idxd)); 77862306a36Sopenharmony_ci err_idxd_alloc: 77962306a36Sopenharmony_ci pci_disable_device(pdev); 78062306a36Sopenharmony_ci return rc; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_civoid idxd_wqs_quiesce(struct idxd_device *idxd) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct idxd_wq *wq; 78662306a36Sopenharmony_ci int i; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci for (i = 0; i < idxd->max_wqs; i++) { 78962306a36Sopenharmony_ci wq = idxd->wqs[i]; 79062306a36Sopenharmony_ci if (wq->state == IDXD_WQ_ENABLED && wq->type == IDXD_WQT_KERNEL) 79162306a36Sopenharmony_ci idxd_wq_quiesce(wq); 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic void idxd_shutdown(struct pci_dev *pdev) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct idxd_device *idxd = pci_get_drvdata(pdev); 79862306a36Sopenharmony_ci struct idxd_irq_entry *irq_entry; 79962306a36Sopenharmony_ci int rc; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci rc = idxd_device_disable(idxd); 80262306a36Sopenharmony_ci if (rc) 80362306a36Sopenharmony_ci dev_err(&pdev->dev, "Disabling device failed\n"); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci irq_entry = &idxd->ie; 80662306a36Sopenharmony_ci synchronize_irq(irq_entry->vector); 80762306a36Sopenharmony_ci idxd_mask_error_interrupts(idxd); 80862306a36Sopenharmony_ci flush_workqueue(idxd->wq); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic void idxd_remove(struct pci_dev *pdev) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct idxd_device *idxd = pci_get_drvdata(pdev); 81462306a36Sopenharmony_ci struct idxd_irq_entry *irq_entry; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci idxd_unregister_devices(idxd); 81762306a36Sopenharmony_ci /* 81862306a36Sopenharmony_ci * When ->release() is called for the idxd->conf_dev, it frees all the memory related 81962306a36Sopenharmony_ci * to the idxd context. The driver still needs those bits in order to do the rest of 82062306a36Sopenharmony_ci * the cleanup. However, we do need to unbound the idxd sub-driver. So take a ref 82162306a36Sopenharmony_ci * on the device here to hold off the freeing while allowing the idxd sub-driver 82262306a36Sopenharmony_ci * to unbind. 82362306a36Sopenharmony_ci */ 82462306a36Sopenharmony_ci get_device(idxd_confdev(idxd)); 82562306a36Sopenharmony_ci device_unregister(idxd_confdev(idxd)); 82662306a36Sopenharmony_ci idxd_shutdown(pdev); 82762306a36Sopenharmony_ci if (device_pasid_enabled(idxd)) 82862306a36Sopenharmony_ci idxd_disable_system_pasid(idxd); 82962306a36Sopenharmony_ci idxd_device_remove_debugfs(idxd); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci irq_entry = idxd_get_ie(idxd, 0); 83262306a36Sopenharmony_ci free_irq(irq_entry->vector, irq_entry); 83362306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 83462306a36Sopenharmony_ci pci_iounmap(pdev, idxd->reg_base); 83562306a36Sopenharmony_ci if (device_user_pasid_enabled(idxd)) 83662306a36Sopenharmony_ci idxd_disable_sva(pdev); 83762306a36Sopenharmony_ci pci_disable_device(pdev); 83862306a36Sopenharmony_ci destroy_workqueue(idxd->wq); 83962306a36Sopenharmony_ci perfmon_pmu_remove(idxd); 84062306a36Sopenharmony_ci put_device(idxd_confdev(idxd)); 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic struct pci_driver idxd_pci_driver = { 84462306a36Sopenharmony_ci .name = DRV_NAME, 84562306a36Sopenharmony_ci .id_table = idxd_pci_tbl, 84662306a36Sopenharmony_ci .probe = idxd_pci_probe, 84762306a36Sopenharmony_ci .remove = idxd_remove, 84862306a36Sopenharmony_ci .shutdown = idxd_shutdown, 84962306a36Sopenharmony_ci}; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic int __init idxd_init_module(void) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci int err; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci /* 85662306a36Sopenharmony_ci * If the CPU does not support MOVDIR64B or ENQCMDS, there's no point in 85762306a36Sopenharmony_ci * enumerating the device. We can not utilize it. 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_ci if (!cpu_feature_enabled(X86_FEATURE_MOVDIR64B)) { 86062306a36Sopenharmony_ci pr_warn("idxd driver failed to load without MOVDIR64B.\n"); 86162306a36Sopenharmony_ci return -ENODEV; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (!cpu_feature_enabled(X86_FEATURE_ENQCMD)) 86562306a36Sopenharmony_ci pr_warn("Platform does not have ENQCMD(S) support.\n"); 86662306a36Sopenharmony_ci else 86762306a36Sopenharmony_ci support_enqcmd = true; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci perfmon_init(); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci err = idxd_driver_register(&idxd_drv); 87262306a36Sopenharmony_ci if (err < 0) 87362306a36Sopenharmony_ci goto err_idxd_driver_register; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci err = idxd_driver_register(&idxd_dmaengine_drv); 87662306a36Sopenharmony_ci if (err < 0) 87762306a36Sopenharmony_ci goto err_idxd_dmaengine_driver_register; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci err = idxd_driver_register(&idxd_user_drv); 88062306a36Sopenharmony_ci if (err < 0) 88162306a36Sopenharmony_ci goto err_idxd_user_driver_register; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci err = idxd_cdev_register(); 88462306a36Sopenharmony_ci if (err) 88562306a36Sopenharmony_ci goto err_cdev_register; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci err = idxd_init_debugfs(); 88862306a36Sopenharmony_ci if (err) 88962306a36Sopenharmony_ci goto err_debugfs; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci err = pci_register_driver(&idxd_pci_driver); 89262306a36Sopenharmony_ci if (err) 89362306a36Sopenharmony_ci goto err_pci_register; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci return 0; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_cierr_pci_register: 89862306a36Sopenharmony_ci idxd_remove_debugfs(); 89962306a36Sopenharmony_cierr_debugfs: 90062306a36Sopenharmony_ci idxd_cdev_remove(); 90162306a36Sopenharmony_cierr_cdev_register: 90262306a36Sopenharmony_ci idxd_driver_unregister(&idxd_user_drv); 90362306a36Sopenharmony_cierr_idxd_user_driver_register: 90462306a36Sopenharmony_ci idxd_driver_unregister(&idxd_dmaengine_drv); 90562306a36Sopenharmony_cierr_idxd_dmaengine_driver_register: 90662306a36Sopenharmony_ci idxd_driver_unregister(&idxd_drv); 90762306a36Sopenharmony_cierr_idxd_driver_register: 90862306a36Sopenharmony_ci return err; 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_cimodule_init(idxd_init_module); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic void __exit idxd_exit_module(void) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci idxd_driver_unregister(&idxd_user_drv); 91562306a36Sopenharmony_ci idxd_driver_unregister(&idxd_dmaengine_drv); 91662306a36Sopenharmony_ci idxd_driver_unregister(&idxd_drv); 91762306a36Sopenharmony_ci pci_unregister_driver(&idxd_pci_driver); 91862306a36Sopenharmony_ci idxd_cdev_remove(); 91962306a36Sopenharmony_ci perfmon_exit(); 92062306a36Sopenharmony_ci idxd_remove_debugfs(); 92162306a36Sopenharmony_ci} 92262306a36Sopenharmony_cimodule_exit(idxd_exit_module); 923