diff mbox series

[v14,1/1] PCI: endpoint: pci-epf-vntb: Use EP MSI controller to handle DB from host

Message ID 20221125192729.1722913-1-Frank.Li@nxp.com
State New
Headers show
Series [v14,1/1] PCI: endpoint: pci-epf-vntb: Use EP MSI controller to handle DB from host | expand

Commit Message

Frank Li Nov. 25, 2022, 7:27 p.m. UTC
┌────────────┐   ┌───────────────────────────────────┐   ┌────────────────┐
│            │   │                                   │   │                │
│            │   │ PCI Endpoint(EP)                  │   │ PCI Host       │
│            │   │                                   │   │                │
│            │◄──┤ 1.platform_msi_domain_alloc_irqs()│   │                │
│            │   │                                   │   │                │
│ MSI        ├──►│ 2.write_msi_msg()                 ├──►├─BAR[DB]        │
│ Controller │   │   update doorbell(DB) register    │   │                │
│            │   │   address for BAR                 │   │                │
│            │   │                                   │   │ 3.Write BAR[DB]│
│            │◄──┼───────────────────────────────────┼───┤                │
│            │   │                                   │   │                │
│            ├──►│ 4.Irq Handle                      │   │                │
│            │   │                                   │   │                │
│            │   │                                   │   │                │
└────────────┘   └───────────────────────────────────┘   └────────────────┘

Add DB support to pci-epf-vntb function driver making use of the platform
MSI controller. If the MSI controller is not available, fallback to the
polling method.

The memory assigned for DB BAR region by the PCI host is mapped to the
message address of platform MSI interrupt controller in PCI EP. Such that,
whenever the PCI host writes to the DB BAR region, it will trigger an IRQ
in the EP.

Work flow is
1. pci-epf-vntb function driver calls platform_msi_domain_alloc_irqs() to
allocate MSI's irq from the platform MSI controller.
2. The epf_ntb_write_msi_msg() passed as a callback will write the offset
of the MSI controller's MSI address dedicated for each MSI to the doorbell
register db_offset and also writes the MSI data to db_data register in the
CTRL BAR region.
3. Finally,the host can trigger doorbell by reading the offset of the
doorbell from db_offset register and writing the data read from db_data
register in CTRL BAR region to the computed address in the DB BAR region.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---

This patch continue https://lore.kernel.org/linux-pci/20220922163206.21281-1-Frank.Li@nxp.com/
The above patch series fork to two parts. clean up and use msi as
doorbell.

clean up patch already been in https://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git/commit/?h=pci/endpoint

These patches based on the above git branch.

Change from v13 to v14
- fixed according to Mani's feedback
- remove first patch PCI: endpoint: pci-epf-vntb: change doorbell
  register offset calc method

Change from v12 to v13:
fixed Lorenzo Pieralisi comments in https://lore.kernel.org/imx/Yz%2FuMiElbqB3ThGd@lpieralisi/T/#u
- update diagram
- Add platform_msi_domain_free_irqs at failure path
- sizeof(u32) is because hardcode by ntb_hw_epf.c


 drivers/pci/endpoint/functions/pci-epf-vntb.c | 168 +++++++++++++++---
 1 file changed, 147 insertions(+), 21 deletions(-)
diff mbox series

Patch

diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index 04698e7995a5..cd97f9f8ea32 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -44,6 +44,7 @@ 
 #include <linux/pci-epc.h>
 #include <linux/pci-epf.h>
 #include <linux/ntb.h>
+#include <linux/msi.h>
 
 static struct workqueue_struct *kpcintb_workqueue;
 
@@ -137,11 +138,14 @@  struct epf_ntb {
 	struct epf_ntb_ctrl *reg;
 
 	u32 *epf_db;
+	phys_addr_t epf_db_phys;
 
 	phys_addr_t vpci_mw_phy[MAX_MW];
 	void __iomem *vpci_mw_addr[MAX_MW];
 
 	struct delayed_work cmd_handler;
+
+	int msi_virqbase;
 };
 
 #define to_epf_ntb(epf_group) container_of((epf_group), struct epf_ntb, group)
@@ -256,11 +260,14 @@  static void epf_ntb_cmd_handler(struct work_struct *work)
 
 	ntb = container_of(work, struct epf_ntb, cmd_handler.work);
 
-	for (i = 1; i < ntb->db_count; i++) {
-		if (ntb->epf_db[i]) {
-			ntb->db |= 1 << (i - 1);
-			ntb_db_event(&ntb->ntb, i);
-			ntb->epf_db[i] = 0;
+	if (!ntb->epf_db_phys) {
+		/* software polling doorbell event */
+		for (i = 1; i < ntb->db_count; i++) {
+			if (ntb->epf_db[i]) {
+				ntb->db |= 1 << (i - 1);
+				ntb_db_event(&ntb->ntb, i);
+				ntb->epf_db[i] = 0;
+			}
 		}
 	}
 
@@ -518,6 +525,28 @@  static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
 	return 0;
 }
 
+static int epf_ntb_db_size(struct epf_ntb *ntb)
+{
+	const struct pci_epc_features *epc_features;
+	size_t size = sizeof(u32) * ntb->db_count;
+	u32 align;
+
+	epc_features = pci_epc_get_features(ntb->epf->epc,
+					    ntb->epf->func_no,
+					    ntb->epf->vfunc_no);
+	align = epc_features->align;
+
+	if (size < 128)
+		size = 128;
+
+	if (align)
+		size = ALIGN(size, align);
+	else
+		size = roundup_pow_of_two(size);
+
+	return size;
+}
+
 /**
  * epf_ntb_db_bar_init() - Configure Doorbell window BARs
  * @ntb: NTB device that facilitates communication between HOST and VHOST
@@ -539,27 +568,26 @@  static int epf_ntb_db_bar_init(struct epf_ntb *ntb)
 					    ntb->epf->func_no,
 					    ntb->epf->vfunc_no);
 	align = epc_features->align;
-
-	if (size < 128)
-		size = 128;
-
-	if (align)
-		size = ALIGN(size, align);
-	else
-		size = roundup_pow_of_two(size);
+	size = epf_ntb_db_size(ntb);
 
 	barno = ntb->epf_ntb_bar[BAR_DB];
+	epf_bar = &ntb->epf->bar[barno];
 
-	mw_addr = pci_epf_alloc_space(ntb->epf, size, barno, align, 0);
-	if (!mw_addr) {
-		dev_err(dev, "Failed to allocate OB address\n");
-		return -ENOMEM;
+	if (ntb->epf_db_phys) {
+		mw_addr = NULL;
+		epf_bar->phys_addr = ntb->epf_db_phys;
+		epf_bar->barno = barno;
+		epf_bar->size = size;
+	} else {
+		mw_addr = pci_epf_alloc_space(ntb->epf, size, barno, align, 0);
+		if (!mw_addr) {
+			dev_err(dev, "Failed to allocate doorbell address\n");
+			return -ENOMEM;
+		}
 	}
 
 	ntb->epf_db = mw_addr;
 
-	epf_bar = &ntb->epf->bar[barno];
-
 	ret = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no, epf_bar);
 	if (ret) {
 		dev_err(dev, "Doorbell BAR set failed\n");
@@ -728,6 +756,100 @@  static int epf_ntb_init_epc_bar(struct epf_ntb *ntb)
 	return 0;
 }
 
+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
+static irqreturn_t epf_ntb_interrupt_handler(int irq, void *data)
+{
+	struct epf_ntb *ntb = data;
+	int index;
+
+	index = irq - ntb->msi_virqbase;
+	ntb->db |= 1 << (index - 1);
+	ntb_db_event(&ntb->ntb, index);
+
+	return IRQ_HANDLED;
+}
+/*
+ * Work flow is
+ *   1. pci-epf-vntb function driver calls platform_msi_domain_alloc_irqs() to
+ *   allocate MSI's irq from the platform MSI controller.
+ *   2. The epf_ntb_write_msi_msg() passed as a callback will write the offset
+ *   of the MSI controller's MSI address dedicated for each MSI to the doorbell
+ *   register db_offset and also writes the MSI data to db_data register in the
+ *   CTRL BAR region.
+ *   3. Finally,the host can trigger doorbell by reading the offset of the
+ *   doorbell from db_offset register and writing the data read from db_data
+ *   register in CTRL BAR region to the computed address in the DB BAR region.
+ */
+static void epf_ntb_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
+{
+	struct epf_ntb *ntb = dev_get_drvdata(desc->dev);
+	struct epf_ntb_ctrl *reg = ntb->reg;
+	int size = epf_ntb_db_size(ntb);
+	u64 addr;
+
+	addr = msg->address_hi;
+	addr <<= 32;
+	addr |= msg->address_lo;
+
+	reg->db_data[desc->msi_index] = msg->data;
+
+	/* ntb->epf_db_phys will be used as DB BAR base address */
+	if (!desc->msi_index)
+		ntb->epf_db_phys = round_down(addr, size);
+
+	/* In drivers/ntb/hw/epf/ntb_hw_epf.c
+	 *
+	 * Doobell register offset in DB BAR is calculated using:
+	 * offset = db_entry_size * interrupt_num + db_offset[interrupt_number]
+	 *
+	 * set db_entry_size as 0 at epf_ntb_epc_msi_init
+	 */
+	reg->db_offset[desc->msi_index] = addr - ntb->epf_db_phys;
+}
+
+static void epf_ntb_epc_msi_init(struct epf_ntb *ntb)
+{
+	struct device *dev = &ntb->epf->dev;
+	struct irq_domain *domain;
+	int virq;
+	u32 old;
+	int ret;
+	int i;
+
+	domain = dev_get_msi_domain(ntb->epf->epc->dev.parent);
+	if (!domain)
+		return;
+
+	dev_set_msi_domain(dev, domain);
+	old = ntb->reg->db_entry_size;
+
+	if (platform_msi_domain_alloc_irqs(&ntb->epf->dev, ntb->db_count, epf_ntb_write_msi_msg)) {
+		dev_dbg(dev, "Can't allocate MSI, falling back to polling mode\n");
+		return;
+	}
+	dev_dbg(dev, "Using MSI as doorbell\n");
+
+	for (i = 0; i < ntb->db_count; i++) {
+		virq = msi_get_virq(dev, i);
+		ret = devm_request_irq(dev, virq,
+			       epf_ntb_interrupt_handler, 0,
+			       "pci_epf_vntb", ntb);
+
+		if (ret) {
+			dev_dbg(dev, "Failed to request doorbell IRQ! Falling back to polling mode");
+			ntb->epf_db_phys = 0;
+			platform_msi_domain_free_irqs(&ntb->epf->dev);
+			ntb->reg->db_entry_size = old;
+			break;
+		}
+
+		if (!i)
+			ntb->msi_virqbase = virq; /* msi start virq number */
+
+		ntb->reg->db_entry_size = 0;
+	}
+}
+#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */
 /**
  * epf_ntb_epc_init() - Initialize NTB interface
  * @ntb: NTB device that facilitates communication between HOST and VHOST
@@ -1336,14 +1458,18 @@  static int epf_ntb_bind(struct pci_epf *epf)
 		goto err_bar_alloc;
 	}
 
+	epf_set_drvdata(epf, ntb);
+
+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
+	epf_ntb_epc_msi_init(ntb);
+#endif
+
 	ret = epf_ntb_epc_init(ntb);
 	if (ret) {
 		dev_err(dev, "Failed to initialize EPC\n");
 		goto err_bar_alloc;
 	}
 
-	epf_set_drvdata(epf, ntb);
-
 	pci_space[0] = (ntb->vntb_pid << 16) | ntb->vntb_vid;
 	pci_vntb_table[0].vendor = ntb->vntb_vid;
 	pci_vntb_table[0].device = ntb->vntb_pid;