[PATCH v3 4/6] PCI: endpoint: pci-epf-test: Add doorbell test support

From: Frank Li
Date: Tue Oct 15 2024 - 18:09:04 EST


Add three registers: doorbell_bar, doorbell_addr, and doorbell_data,
along with doorbell_done. Use pci_epf_alloc_doorbell() to allocate a
doorbell address space.

Enable the Root Complex (RC) side driver to trigger pci-epc-test's doorbell
callback handler by writing doorbell_data to the mapped doorbell_bar's
address space.

Set doorbell_done in the doorbell callback to indicate completion.

To avoid broken compatibility, use new PID/VID and set RevID bigger than 0.
So only new pcitest program can distinguish with/without doorbell support
and avoid wrongly write test data to doorbell bar.

Signed-off-by: Frank Li <Frank.Li@xxxxxxx>
---
drivers/pci/endpoint/functions/pci-epf-test.c | 58 ++++++++++++++++++++++++++-
1 file changed, 56 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index 7c2ed6eae53ad..c054d621353a6 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -11,12 +11,14 @@
#include <linux/dmaengine.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/msi.h>
#include <linux/slab.h>
#include <linux/pci_ids.h>
#include <linux/random.h>

#include <linux/pci-epc.h>
#include <linux/pci-epf.h>
+#include <linux/pci-ep-msi.h>
#include <linux/pci_regs.h>

#define IRQ_TYPE_INTX 0
@@ -39,6 +41,7 @@
#define STATUS_IRQ_RAISED BIT(6)
#define STATUS_SRC_ADDR_INVALID BIT(7)
#define STATUS_DST_ADDR_INVALID BIT(8)
+#define STATUS_DOORBELL_SUCCESS BIT(9)

#define FLAG_USE_DMA BIT(0)

@@ -50,6 +53,7 @@ struct pci_epf_test {
void *reg[PCI_STD_NUM_BARS];
struct pci_epf *epf;
enum pci_barno test_reg_bar;
+ enum pci_barno doorbell_bar;
size_t msix_table_offset;
struct delayed_work cmd_handler;
struct dma_chan *dma_chan_tx;
@@ -74,6 +78,9 @@ struct pci_epf_test_reg {
u32 irq_type;
u32 irq_number;
u32 flags;
+ u32 doorbell_bar;
+ u32 doorbell_addr;
+ u32 doorbell_data;
} __packed;

static struct pci_epf_header test_header = {
@@ -695,7 +702,7 @@ static int pci_epf_test_set_bar(struct pci_epf *epf)
enum pci_barno test_reg_bar = epf_test->test_reg_bar;

for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
- if (!epf_test->reg[bar])
+ if (!epf_test->reg[bar] && bar != epf_test->doorbell_bar)
continue;

ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no,
@@ -810,11 +817,24 @@ static int pci_epf_test_link_down(struct pci_epf *epf)
return 0;
}

+static int pci_epf_test_doorbell(struct pci_epf *epf, int index)
+{
+ struct pci_epf_test *epf_test = epf_get_drvdata(epf);
+ enum pci_barno test_reg_bar = epf_test->test_reg_bar;
+ struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
+
+ reg->status |= STATUS_DOORBELL_SUCCESS;
+ pci_epf_test_raise_irq(epf_test, reg);
+
+ return 0;
+}
+
static const struct pci_epc_event_ops pci_epf_test_event_ops = {
.epc_init = pci_epf_test_epc_init,
.epc_deinit = pci_epf_test_epc_deinit,
.link_up = pci_epf_test_link_up,
.link_down = pci_epf_test_link_down,
+ .doorbell = pci_epf_test_doorbell,
};

static int pci_epf_test_alloc_space(struct pci_epf *epf)
@@ -853,7 +873,7 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
if (bar == NO_BAR)
break;

- if (bar == test_reg_bar)
+ if (bar == test_reg_bar || bar == epf_test->doorbell_bar)
continue;

base = pci_epf_alloc_space(epf, bar_size[bar], bar,
@@ -887,7 +907,11 @@ static int pci_epf_test_bind(struct pci_epf *epf)
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
const struct pci_epc_features *epc_features;
enum pci_barno test_reg_bar = BAR_0;
+ enum pci_barno doorbell_bar = NO_BAR;
struct pci_epc *epc = epf->epc;
+ struct msi_msg *msg;
+ u64 doorbell_addr;
+ u32 align;

if (WARN_ON_ONCE(!epc))
return -EINVAL;
@@ -905,10 +929,40 @@ static int pci_epf_test_bind(struct pci_epf *epf)
epf_test->test_reg_bar = test_reg_bar;
epf_test->epc_features = epc_features;

+ align = epc_features->align;
+ align = align ? align : 128;
+
+ /* Only revid >=1 support RC-to-EP Door bell */
+ ret = epf->header->revid > 0 ? pci_epf_alloc_doorbell(epf, 1) : -EINVAL;
+ if (!ret) {
+ msg = epf->msg;
+ doorbell_bar = pci_epc_get_next_free_bar(epc_features, test_reg_bar + 1);
+
+ if (doorbell_bar > 0) {
+ epf_test->doorbell_bar = doorbell_bar;
+ doorbell_addr = msg->address_hi;
+ doorbell_addr <<= 32;
+ doorbell_addr |= msg->address_lo;
+ epf->bar[doorbell_bar].phys_addr = round_down(doorbell_addr, align);
+ epf->bar[doorbell_bar].barno = doorbell_bar;
+ epf->bar[doorbell_bar].size = align;
+ } else {
+ pci_epf_free_doorbell(epf);
+ }
+ }
+
ret = pci_epf_test_alloc_space(epf);
if (ret)
return ret;

+ if (doorbell_bar > 0) {
+ struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
+
+ reg->doorbell_addr = doorbell_addr & (align - 1);
+ reg->doorbell_data = msg->data;
+ reg->doorbell_bar = doorbell_bar;
+ }
+
return 0;
}


--
2.34.1