xref: /linux/drivers/pci/endpoint/pci-ep-msi.c (revision b4ada0618eed0fbd1b1630f73deb048c592b06a1)
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