mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
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:
parent
19272b37aa
commit
1c3b002c6b
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
92
drivers/pci/endpoint/pci-ep-msi.c
Normal file
92
drivers/pci/endpoint/pci-ep-msi.c
Normal 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);
|
||||||
28
include/linux/pci-ep-msi.h
Normal file
28
include/linux/pci-ep-msi.h
Normal 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__ */
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user