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
This commit is contained in:
Frank Li 2025-07-10 15:13:49 -04:00 committed by Bjorn Helgaas
parent 19272b37aa
commit 1c3b002c6b
5 changed files with 144 additions and 0 deletions

View File

@ -28,6 +28,14 @@ config PCI_ENDPOINT_CONFIGFS
configure the endpoint function and used to bind the configure the endpoint function and used to bind the
function with an endpoint controller. function with an endpoint controller.
config PCI_ENDPOINT_MSI_DOORBELL
bool "PCI Endpoint MSI Doorbell Support"
depends on PCI_ENDPOINT && GENERIC_MSI_IRQ
help
This enables the EP's MSI interrupt controller to function as a
doorbell. The RC can trigger doorbell in EP by writing data to a
dedicated BAR, which the EP maps to the controller's message address.
source "drivers/pci/endpoint/functions/Kconfig" source "drivers/pci/endpoint/functions/Kconfig"
endmenu endmenu

View File

@ -6,3 +6,4 @@
obj-$(CONFIG_PCI_ENDPOINT_CONFIGFS) += pci-ep-cfs.o obj-$(CONFIG_PCI_ENDPOINT_CONFIGFS) += pci-ep-cfs.o
obj-$(CONFIG_PCI_ENDPOINT) += pci-epc-core.o pci-epf-core.o\ obj-$(CONFIG_PCI_ENDPOINT) += pci-epc-core.o pci-epf-core.o\
pci-epc-mem.o functions/ pci-epc-mem.o functions/
obj-$(CONFIG_PCI_ENDPOINT_MSI_DOORBELL) += pci-ep-msi.o

View File

@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PCI Endpoint *Controller* (EPC) MSI library
*
* Copyright (C) 2025 NXP
* Author: Frank Li <Frank.Li@nxp.com>
*/
#include <linux/device.h>
#include <linux/export.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/of_irq.h>
#include <linux/pci-epc.h>
#include <linux/pci-epf.h>
#include <linux/pci-ep-cfs.h>
#include <linux/pci-ep-msi.h>
#include <linux/slab.h>
static void pci_epf_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
{
struct pci_epc *epc;
struct pci_epf *epf;
epc = pci_epc_get(dev_name(msi_desc_to_dev(desc)));
if (!epc)
return;
epf = list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list);
if (epf && epf->db_msg && desc->msi_index < epf->num_db)
memcpy(&epf->db_msg[desc->msi_index].msg, msg, sizeof(*msg));
pci_epc_put(epc);
}
int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
{
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
struct irq_domain *domain;
void *msg;
int ret;
int i;
/* TODO: Multi-EPF support */
if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) {
dev_err(dev, "MSI doorbell doesn't support multiple EPF\n");
return -EINVAL;
}
domain = of_msi_map_get_device_domain(epc->dev.parent, 0,
DOMAIN_BUS_PLATFORM_MSI);
if (!domain) {
dev_err(dev, "Can't find MSI domain for EPC\n");
return -ENODEV;
}
dev_set_msi_domain(epc->dev.parent, domain);
msg = kcalloc(num_db, sizeof(struct pci_epf_doorbell_msg), GFP_KERNEL);
if (!msg)
return -ENOMEM;
epf->num_db = num_db;
epf->db_msg = msg;
ret = platform_device_msi_init_and_alloc_irqs(epc->dev.parent, num_db,
pci_epf_write_msi_msg);
if (ret) {
dev_err(dev, "Failed to allocate MSI\n");
kfree(msg);
return ret;
}
for (i = 0; i < num_db; i++)
epf->db_msg[i].virq = msi_get_virq(epc->dev.parent, i);
return ret;
}
EXPORT_SYMBOL_GPL(pci_epf_alloc_doorbell);
void pci_epf_free_doorbell(struct pci_epf *epf)
{
platform_device_msi_free_irqs_all(epf->epc->dev.parent);
kfree(epf->db_msg);
epf->db_msg = NULL;
epf->num_db = 0;
}
EXPORT_SYMBOL_GPL(pci_epf_free_doorbell);

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* PCI Endpoint *Function* side MSI header file
*
* Copyright (C) 2024 NXP
* Author: Frank Li <Frank.Li@nxp.com>
*/
#ifndef __PCI_EP_MSI__
#define __PCI_EP_MSI__
struct pci_epf;
#ifdef CONFIG_PCI_ENDPOINT_MSI_DOORBELL
int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 nums);
void pci_epf_free_doorbell(struct pci_epf *epf);
#else
static inline int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 nums)
{
return -ENODATA;
}
static inline void pci_epf_free_doorbell(struct pci_epf *epf)
{
}
#endif /* CONFIG_GENERIC_MSI_IRQ */
#endif /* __PCI_EP_MSI__ */

View File

@ -12,6 +12,7 @@
#include <linux/configfs.h> #include <linux/configfs.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/msi.h>
#include <linux/pci.h> #include <linux/pci.h>
struct pci_epf; struct pci_epf;
@ -128,6 +129,16 @@ struct pci_epf_bar {
int flags; int flags;
}; };
/**
* struct pci_epf_doorbell_msg - represents doorbell message
* @msg: MSI message
* @virq: IRQ number of this doorbell MSI message
*/
struct pci_epf_doorbell_msg {
struct msi_msg msg;
int virq;
};
/** /**
* struct pci_epf - represents the PCI EPF device * struct pci_epf - represents the PCI EPF device
* @dev: the PCI EPF device * @dev: the PCI EPF device
@ -155,6 +166,8 @@ struct pci_epf_bar {
* @vfunction_num_map: bitmap to manage virtual function number * @vfunction_num_map: bitmap to manage virtual function number
* @pci_vepf: list of virtual endpoint functions associated with this function * @pci_vepf: list of virtual endpoint functions associated with this function
* @event_ops: callbacks for capturing the EPC events * @event_ops: callbacks for capturing the EPC events
* @db_msg: data for MSI from RC side
* @num_db: number of doorbells
*/ */
struct pci_epf { struct pci_epf {
struct device dev; struct device dev;
@ -185,6 +198,8 @@ struct pci_epf {
unsigned long vfunction_num_map; unsigned long vfunction_num_map;
struct list_head pci_vepf; struct list_head pci_vepf;
const struct pci_epc_event_ops *event_ops; const struct pci_epc_event_ops *event_ops;
struct pci_epf_doorbell_msg *db_msg;
u16 num_db;
}; };
/** /**