Add three registers: PCIE_ENDPOINT_TEST_DB_BAR, PCIE_ENDPOINT_TEST_DB_ADDR,
and PCIE_ENDPOINT_TEST_DB_DATA.

Trigger the doorbell by writing data from PCI_ENDPOINT_TEST_DB_DATA to the
address provided by PCI_ENDPOINT_TEST_DB_OFFSET and wait for endpoint
feedback.

Add two command to COMMAND_ENABLE_DOORBELL and COMMAND_DISABLE_DOORBELL
to enable EP side's doorbell support and avoid compatible problem, which
host side driver miss-match with endpoint side function driver. See below
table:

                Host side new driver    Host side old driver
EP: new driver          S                       F
EP: old driver          F                       F

S: If EP side support MSI, 'pci_endpoint_test -f pcie_ep_doorbell' return
success.
   If EP side doesn't support MSI, the same to 'F'.

F: 'pci_endpoint_test -f pcie_ep_doorbell' return failure, other case as
usual.

Tested-by: Niklas Cassel <cas...@kernel.org>
Signed-off-by: Frank Li <frank...@nxp.com>
---
change from v14 to v16
- none

Change from v13 to v14
- update to use pci_endpoint_test -f pcie_ep_doorbell
- change ioctrl id to fix conflict

Change from v9 to v13
- none

Change from v8 to v9
- change PCITEST_DOORBELL to 0xa

Change form v6 to v8
- none

Change from v5 to v6
- %s/PCI_ENDPOINT_TEST_DB_ADDR/PCI_ENDPOINT_TEST_DB_OFFSET/g

Change from v4 to v5
- remove unused varible
- add irq_type at pci_endpoint_test_doorbell();

change from v3 to v4
- Add COMMAND_ENABLE_DOORBELL and COMMAND_DISABLE_DOORBELL.
- Remove new DID requirement.
---
 drivers/misc/pci_endpoint_test.c | 82 ++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/pcitest.h     |  1 +
 2 files changed, 83 insertions(+)

diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
index c4e5e2c977be2..0f3af7adea107 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -37,6 +37,8 @@
 #define COMMAND_READ                           BIT(3)
 #define COMMAND_WRITE                          BIT(4)
 #define COMMAND_COPY                           BIT(5)
+#define COMMAND_ENABLE_DOORBELL                        BIT(6)
+#define COMMAND_DISABLE_DOORBELL               BIT(7)
 
 #define PCI_ENDPOINT_TEST_STATUS               0x8
 #define STATUS_READ_SUCCESS                    BIT(0)
@@ -48,6 +50,11 @@
 #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 STATUS_DOORBELL_ENABLE_SUCCESS         BIT(10)
+#define STATUS_DOORBELL_ENABLE_FAIL            BIT(11)
+#define STATUS_DOORBELL_DISABLE_SUCCESS                BIT(12)
+#define STATUS_DOORBELL_DISABLE_FAIL           BIT(13)
 
 #define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR       0x0c
 #define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR       0x10
@@ -62,6 +69,7 @@
 #define PCI_ENDPOINT_TEST_IRQ_NUMBER           0x28
 
 #define PCI_ENDPOINT_TEST_FLAGS                        0x2c
+
 #define FLAG_USE_DMA                           BIT(0)
 
 #define PCI_ENDPOINT_TEST_CAPS                 0x30
@@ -70,6 +78,10 @@
 #define CAP_MSIX                               BIT(2)
 #define CAP_INTX                               BIT(3)
 
+#define PCI_ENDPOINT_TEST_DB_BAR               0x34
+#define PCI_ENDPOINT_TEST_DB_OFFSET            0x38
+#define PCI_ENDPOINT_TEST_DB_DATA              0x3c
+
 #define PCI_DEVICE_ID_TI_AM654                 0xb00c
 #define PCI_DEVICE_ID_TI_J7200                 0xb00f
 #define PCI_DEVICE_ID_TI_AM64                  0xb010
@@ -100,6 +112,7 @@ enum pci_barno {
        BAR_3,
        BAR_4,
        BAR_5,
+       NO_BAR = -1,
 };
 
 struct pci_endpoint_test {
@@ -841,6 +854,72 @@ static int pci_endpoint_test_set_irq(struct 
pci_endpoint_test *test,
        return 0;
 }
 
+static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test)
+{
+       struct pci_dev *pdev = test->pdev;
+       struct device *dev = &pdev->dev;
+       int irq_type = test->irq_type;
+       enum pci_barno bar;
+       u32 data, status;
+       u32 addr;
+
+       if (irq_type < PCITEST_IRQ_TYPE_INTX ||
+           irq_type > PCITEST_IRQ_TYPE_MSIX) {
+               dev_err(dev, "Invalid IRQ type option\n");
+               return -EINVAL;
+       }
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+                                COMMAND_ENABLE_DOORBELL);
+
+       wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000));
+
+       status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
+       if (status & STATUS_DOORBELL_ENABLE_FAIL) {
+               dev_err(dev, "Failed to enable doorbell\n");
+               return -EINVAL;
+       }
+
+       data = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_DATA);
+       addr = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_OFFSET);
+       bar = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_BAR);
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0);
+
+       bar = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_BAR);
+
+       writel(data, test->bar[bar] + addr);
+
+       wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000));
+
+       status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
+
+       if (!(status & STATUS_DOORBELL_SUCCESS))
+               dev_err(dev, "Endpoint have not received Doorbell\n");
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+                                COMMAND_DISABLE_DOORBELL);
+
+       wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000));
+
+       status |= pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
+
+       if (status & STATUS_DOORBELL_DISABLE_FAIL) {
+               dev_err(dev, "Failed to disable doorbell\n");
+               return -EINVAL;
+       }
+
+       if (!(status & STATUS_DOORBELL_SUCCESS))
+               return -EINVAL;
+
+       return 0;
+}
+
 static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
                                    unsigned long arg)
 {
@@ -891,6 +970,9 @@ static long pci_endpoint_test_ioctl(struct file *file, 
unsigned int cmd,
        case PCITEST_CLEAR_IRQ:
                ret = pci_endpoint_test_clear_irq(test);
                break;
+       case PCITEST_DOORBELL:
+               ret = pci_endpoint_test_doorbell(test);
+               break;
        }
 
 ret:
diff --git a/include/uapi/linux/pcitest.h b/include/uapi/linux/pcitest.h
index d3aa8715a525e..d6023a45a9d03 100644
--- a/include/uapi/linux/pcitest.h
+++ b/include/uapi/linux/pcitest.h
@@ -21,6 +21,7 @@
 #define PCITEST_SET_IRQTYPE    _IOW('P', 0x8, int)
 #define PCITEST_GET_IRQTYPE    _IO('P', 0x9)
 #define PCITEST_BARS           _IO('P', 0xa)
+#define PCITEST_DOORBELL       _IO('P', 0xb)
 #define PCITEST_CLEAR_IRQ      _IO('P', 0x10)
 
 #define PCITEST_IRQ_TYPE_UNDEFINED     -1

-- 
2.34.1


Reply via email to