1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * PCI Endpoint *Controller* (EPC) MSI library 4 * 5 * Copyright (C) 2025 NXP 6 * Author: Frank Li <Frank.Li@nxp.com> 7 */ 8 9 #include <linux/device.h> 10 #include <linux/export.h> 11 #include <linux/irqdomain.h> 12 #include <linux/module.h> 13 #include <linux/msi.h> 14 #include <linux/of_irq.h> 15 #include <linux/pci-epc.h> 16 #include <linux/pci-epf.h> 17 #include <linux/pci-ep-cfs.h> 18 #include <linux/pci-ep-msi.h> 19 #include <linux/slab.h> 20 21 static void pci_epf_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) 22 { 23 struct pci_epc *epc; 24 struct pci_epf *epf; 25 26 epc = pci_epc_get(dev_name(msi_desc_to_dev(desc))); 27 if (!epc) 28 return; 29 30 epf = list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list); 31 32 if (epf && epf->db_msg && desc->msi_index < epf->num_db) 33 memcpy(&epf->db_msg[desc->msi_index].msg, msg, sizeof(*msg)); 34 35 pci_epc_put(epc); 36 } 37 38 int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) 39 { 40 struct pci_epc *epc = epf->epc; 41 struct device *dev = &epf->dev; 42 struct irq_domain *domain; 43 void *msg; 44 int ret; 45 int i; 46 47 /* TODO: Multi-EPF support */ 48 if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) { 49 dev_err(dev, "MSI doorbell doesn't support multiple EPF\n"); 50 return -EINVAL; 51 } 52 53 domain = of_msi_map_get_device_domain(epc->dev.parent, 0, 54 DOMAIN_BUS_PLATFORM_MSI); 55 if (!domain) { 56 dev_err(dev, "Can't find MSI domain for EPC\n"); 57 return -ENODEV; 58 } 59 60 if (!irq_domain_is_msi_parent(domain)) 61 return -ENODEV; 62 63 if (!irq_domain_is_msi_immutable(domain)) { 64 dev_err(dev, "Mutable MSI controller not supported\n"); 65 return -ENODEV; 66 } 67 68 dev_set_msi_domain(epc->dev.parent, domain); 69 70 msg = kcalloc(num_db, sizeof(struct pci_epf_doorbell_msg), GFP_KERNEL); 71 if (!msg) 72 return -ENOMEM; 73 74 epf->num_db = num_db; 75 epf->db_msg = msg; 76 77 ret = platform_device_msi_init_and_alloc_irqs(epc->dev.parent, num_db, 78 pci_epf_write_msi_msg); 79 if (ret) { 80 dev_err(dev, "Failed to allocate MSI\n"); 81 kfree(msg); 82 return ret; 83 } 84 85 for (i = 0; i < num_db; i++) 86 epf->db_msg[i].virq = msi_get_virq(epc->dev.parent, i); 87 88 return ret; 89 } 90 EXPORT_SYMBOL_GPL(pci_epf_alloc_doorbell); 91 92 void pci_epf_free_doorbell(struct pci_epf *epf) 93 { 94 platform_device_msi_free_irqs_all(epf->epc->dev.parent); 95 96 kfree(epf->db_msg); 97 epf->db_msg = NULL; 98 epf->num_db = 0; 99 } 100 EXPORT_SYMBOL_GPL(pci_epf_free_doorbell); 101