Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

PCI: endpoint: Add RC-to-EP doorbell support using platform MSI controller

Implement the doorbell feature by mapping the EP's MSI interrupt controller
message address to a dedicated BAR.

The EPF driver should pass the actual message data to be written to the
message address by the host through implementation-specific logic.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
[mani: minor code cleanups and reworded commit message]
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
[bhelgaas: fix kernel-doc]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Niklas Cassel <cassel@kernel.org>
Link: https://patch.msgid.link/20250710-ep-msi-v21-3-57683fc7fb25@nxp.com

authored by

Frank Li and committed by
Bjorn Helgaas
1c3b002c 19272b37

+144
+8
drivers/pci/endpoint/Kconfig
··· 28 28 configure the endpoint function and used to bind the 29 29 function with an endpoint controller. 30 30 31 + config PCI_ENDPOINT_MSI_DOORBELL 32 + bool "PCI Endpoint MSI Doorbell Support" 33 + depends on PCI_ENDPOINT && GENERIC_MSI_IRQ 34 + help 35 + This enables the EP's MSI interrupt controller to function as a 36 + doorbell. The RC can trigger doorbell in EP by writing data to a 37 + dedicated BAR, which the EP maps to the controller's message address. 38 + 31 39 source "drivers/pci/endpoint/functions/Kconfig" 32 40 33 41 endmenu
+1
drivers/pci/endpoint/Makefile
··· 6 6 obj-$(CONFIG_PCI_ENDPOINT_CONFIGFS) += pci-ep-cfs.o 7 7 obj-$(CONFIG_PCI_ENDPOINT) += pci-epc-core.o pci-epf-core.o\ 8 8 pci-epc-mem.o functions/ 9 + obj-$(CONFIG_PCI_ENDPOINT_MSI_DOORBELL) += pci-ep-msi.o
+92
drivers/pci/endpoint/pci-ep-msi.c
··· 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 + dev_set_msi_domain(epc->dev.parent, domain); 61 + 62 + msg = kcalloc(num_db, sizeof(struct pci_epf_doorbell_msg), GFP_KERNEL); 63 + if (!msg) 64 + return -ENOMEM; 65 + 66 + epf->num_db = num_db; 67 + epf->db_msg = msg; 68 + 69 + ret = platform_device_msi_init_and_alloc_irqs(epc->dev.parent, num_db, 70 + pci_epf_write_msi_msg); 71 + if (ret) { 72 + dev_err(dev, "Failed to allocate MSI\n"); 73 + kfree(msg); 74 + return ret; 75 + } 76 + 77 + for (i = 0; i < num_db; i++) 78 + epf->db_msg[i].virq = msi_get_virq(epc->dev.parent, i); 79 + 80 + return ret; 81 + } 82 + EXPORT_SYMBOL_GPL(pci_epf_alloc_doorbell); 83 + 84 + void pci_epf_free_doorbell(struct pci_epf *epf) 85 + { 86 + platform_device_msi_free_irqs_all(epf->epc->dev.parent); 87 + 88 + kfree(epf->db_msg); 89 + epf->db_msg = NULL; 90 + epf->num_db = 0; 91 + } 92 + EXPORT_SYMBOL_GPL(pci_epf_free_doorbell);
+28
include/linux/pci-ep-msi.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * PCI Endpoint *Function* side MSI header file 4 + * 5 + * Copyright (C) 2024 NXP 6 + * Author: Frank Li <Frank.Li@nxp.com> 7 + */ 8 + 9 + #ifndef __PCI_EP_MSI__ 10 + #define __PCI_EP_MSI__ 11 + 12 + struct pci_epf; 13 + 14 + #ifdef CONFIG_PCI_ENDPOINT_MSI_DOORBELL 15 + int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 nums); 16 + void pci_epf_free_doorbell(struct pci_epf *epf); 17 + #else 18 + static inline int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 nums) 19 + { 20 + return -ENODATA; 21 + } 22 + 23 + static inline void pci_epf_free_doorbell(struct pci_epf *epf) 24 + { 25 + } 26 + #endif /* CONFIG_GENERIC_MSI_IRQ */ 27 + 28 + #endif /* __PCI_EP_MSI__ */
+15
include/linux/pci-epf.h
··· 12 12 #include <linux/configfs.h> 13 13 #include <linux/device.h> 14 14 #include <linux/mod_devicetable.h> 15 + #include <linux/msi.h> 15 16 #include <linux/pci.h> 16 17 17 18 struct pci_epf; ··· 130 129 }; 131 130 132 131 /** 132 + * struct pci_epf_doorbell_msg - represents doorbell message 133 + * @msg: MSI message 134 + * @virq: IRQ number of this doorbell MSI message 135 + */ 136 + struct pci_epf_doorbell_msg { 137 + struct msi_msg msg; 138 + int virq; 139 + }; 140 + 141 + /** 133 142 * struct pci_epf - represents the PCI EPF device 134 143 * @dev: the PCI EPF device 135 144 * @name: the name of the PCI EPF device ··· 166 155 * @vfunction_num_map: bitmap to manage virtual function number 167 156 * @pci_vepf: list of virtual endpoint functions associated with this function 168 157 * @event_ops: callbacks for capturing the EPC events 158 + * @db_msg: data for MSI from RC side 159 + * @num_db: number of doorbells 169 160 */ 170 161 struct pci_epf { 171 162 struct device dev; ··· 198 185 unsigned long vfunction_num_map; 199 186 struct list_head pci_vepf; 200 187 const struct pci_epc_event_ops *event_ops; 188 + struct pci_epf_doorbell_msg *db_msg; 189 + u16 num_db; 201 190 }; 202 191 203 192 /**