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
pci_epf_write_msi_msg(struct msi_desc * desc,struct msi_msg * msg)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 (IS_ERR(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
pci_epf_alloc_doorbell(struct pci_epf * epf,u16 num_db)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
pci_epf_free_doorbell(struct pci_epf * epf)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