Adds the MSI-X support and updates driver documentation accordingly. Changes the driver parameter in order to allow the interruption type selection. Signed-off-by: Gustavo Pimentel <gustavo.pimentel@xxxxxxxxxxxx> --- Documentation/misc-devices/pci-endpoint-test.txt | 3 + drivers/misc/pci_endpoint_test.c | 102 +++++++++++++++++------ 2 files changed, 79 insertions(+), 26 deletions(-) diff --git a/Documentation/misc-devices/pci-endpoint-test.txt b/Documentation/misc-devices/pci-endpoint-test.txt index 4ebc359..fdfa0f6 100644 --- a/Documentation/misc-devices/pci-endpoint-test.txt +++ b/Documentation/misc-devices/pci-endpoint-test.txt @@ -10,6 +10,7 @@ The PCI driver for the test device performs the following tests *) verifying addresses programmed in BAR *) raise legacy IRQ *) raise MSI IRQ + *) raise MSI-X IRQ *) read data *) write data *) copy data @@ -25,6 +26,8 @@ ioctl PCITEST_LEGACY_IRQ: Tests legacy IRQ PCITEST_MSI: Tests message signalled interrupts. The MSI number to be tested should be passed as argument. + PCITEST_MSIX: Tests message signalled interrupts. The MSI-X number + to be tested should be passed as argument. PCITEST_WRITE: Perform write tests. The size of the buffer should be passed as argument. PCITEST_READ: Perform read tests. The size of the buffer should be passed diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 37db0fc..a7d9354 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -42,11 +42,16 @@ #define PCI_ENDPOINT_TEST_COMMAND 0x4 #define COMMAND_RAISE_LEGACY_IRQ BIT(0) #define COMMAND_RAISE_MSI_IRQ BIT(1) -#define MSI_NUMBER_SHIFT 2 -/* 6 bits for MSI number */ -#define COMMAND_READ BIT(8) -#define COMMAND_WRITE BIT(9) -#define COMMAND_COPY BIT(10) +#define COMMAND_RAISE_MSIX_IRQ BIT(2) +#define IRQ_TYPE_SHIFT 3 +#define IRQ_TYPE_LEGACY 0 +#define IRQ_TYPE_MSI 1 +#define IRQ_TYPE_MSIX 2 +#define MSI_NUMBER_SHIFT 5 +/* 12 bits for MSI number */ +#define COMMAND_READ BIT(17) +#define COMMAND_WRITE BIT(18) +#define COMMAND_COPY BIT(19) #define PCI_ENDPOINT_TEST_STATUS 0x8 #define STATUS_READ_SUCCESS BIT(0) @@ -73,9 +78,9 @@ static DEFINE_IDA(pci_endpoint_test_ida); #define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \ miscdev) -static bool no_msi; -module_param(no_msi, bool, 0444); -MODULE_PARM_DESC(no_msi, "Disable MSI interrupt in pci_endpoint_test"); +static int irq_type = IRQ_TYPE_MSIX; +module_param(irq_type, int, 0444); +MODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI, 2 - MSI-X)"); enum pci_barno { BAR_0, @@ -103,7 +108,7 @@ struct pci_endpoint_test { struct pci_endpoint_test_data { enum pci_barno test_reg_bar; size_t alignment; - bool no_msi; + int irq_type; }; static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test, @@ -177,10 +182,10 @@ static bool pci_endpoint_test_bar(struct pci_endpoint_test *test, static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test) { - u32 val; + u32 val = COMMAND_RAISE_LEGACY_IRQ; - pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, - COMMAND_RAISE_LEGACY_IRQ); + val |= (IRQ_TYPE_LEGACY << IRQ_TYPE_SHIFT); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, val); val = wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000)); if (!val) @@ -192,12 +197,12 @@ static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test) static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, u8 msi_num) { - u32 val; + u32 val = COMMAND_RAISE_MSI_IRQ; struct pci_dev *pdev = test->pdev; - pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, - msi_num << MSI_NUMBER_SHIFT | - COMMAND_RAISE_MSI_IRQ); + val |= (msi_num << MSI_NUMBER_SHIFT); + val |= (IRQ_TYPE_MSI << IRQ_TYPE_SHIFT); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, val); val = wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000)); if (!val) @@ -209,6 +214,26 @@ static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, return false; } +static bool pci_endpoint_test_msix_irq(struct pci_endpoint_test *test, + u16 msix_num) +{ + u32 val = COMMAND_RAISE_MSIX_IRQ; + struct pci_dev *pdev = test->pdev; + + val |= (msix_num << MSI_NUMBER_SHIFT); + val |= (IRQ_TYPE_MSIX << IRQ_TYPE_SHIFT); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, val); + val = wait_for_completion_timeout(&test->irq_raised, + msecs_to_jiffies(1000)); + if (!val) + return false; + + if (test->last_irq - pdev->irq == msix_num - 1) + return true; + + return false; +} + static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size) { bool ret = false; @@ -226,6 +251,7 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size) size_t alignment = test->alignment; u32 src_crc32; u32 dst_crc32; + u32 val = COMMAND_COPY; if (size > SIZE_MAX - alignment) goto err; @@ -281,8 +307,9 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size) pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size); - pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, - 1 << MSI_NUMBER_SHIFT | COMMAND_COPY); + val |= (1 << MSI_NUMBER_SHIFT); + val |= (irq_type << IRQ_TYPE_SHIFT); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, val); wait_for_completion(&test->irq_raised); @@ -314,6 +341,7 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size) size_t offset; size_t alignment = test->alignment; u32 crc32; + u32 val = COMMAND_READ; if (size > SIZE_MAX - alignment) goto err; @@ -348,8 +376,9 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size) pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size); - pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, - 1 << MSI_NUMBER_SHIFT | COMMAND_READ); + val |= (1 << MSI_NUMBER_SHIFT); + val |= (irq_type << IRQ_TYPE_SHIFT); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, val); wait_for_completion(&test->irq_raised); @@ -375,6 +404,7 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size) size_t offset; size_t alignment = test->alignment; u32 crc32; + u32 val = COMMAND_WRITE; if (size > SIZE_MAX - alignment) goto err; @@ -403,8 +433,9 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size) pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size); - pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, - 1 << MSI_NUMBER_SHIFT | COMMAND_WRITE); + val |= (1 << MSI_NUMBER_SHIFT); + val |= (irq_type << IRQ_TYPE_SHIFT); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, val); wait_for_completion(&test->irq_raised); @@ -438,6 +469,9 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, case PCITEST_MSI: ret = pci_endpoint_test_msi_irq(test, arg); break; + case PCITEST_MSIX: + ret = pci_endpoint_test_msix_irq(test, arg); + break; case PCITEST_WRITE: ret = pci_endpoint_test_write(test, arg); break; @@ -490,7 +524,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, if (data) { test_reg_bar = data->test_reg_bar; test->alignment = data->alignment; - no_msi = data->no_msi; + irq_type = data->irq_type; } init_completion(&test->irq_raised); @@ -510,11 +544,24 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, pci_set_master(pdev); - if (!no_msi) { + switch (irq_type) { + case IRQ_TYPE_LEGACY: + break; + case IRQ_TYPE_MSI: irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI); if (irq < 0) dev_err(dev, "failed to get MSI interrupts\n"); test->num_irqs = irq; + break; + case IRQ_TYPE_MSIX: + irq = pci_alloc_irq_vectors(pdev, 1, 2048, PCI_IRQ_MSIX); + if (irq < 0) + dev_err(dev, "Failed to get MSI-X interrupts\n"); + test->num_irqs = irq; + break; + default: + dev_err(dev, "Invalid IRQ type selected\n"); + goto err_disable_msi; } err = devm_request_irq(dev, pdev->irq, pci_endpoint_test_irqhandler, @@ -529,8 +576,9 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, pci_endpoint_test_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, test); if (err) - dev_err(dev, "failed to request IRQ %d for MSI %d\n", - pdev->irq + i, i + 1); + dev_err(dev, "Failed to request IRQ %d for MSI%s %d\n", + pdev->irq + i, + irq_type == IRQ_TYPE_MSIX ? "-X" : "", i + 1); } for (bar = BAR_0; bar <= BAR_5; bar++) { @@ -596,6 +644,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, err_disable_msi: pci_disable_msi(pdev); + pci_disable_msix(pdev); pci_release_regions(pdev); err_disable_pdev: @@ -627,6 +676,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) for (i = 0; i < test->num_irqs; i++) devm_free_irq(&pdev->dev, pdev->irq + i, test); pci_disable_msi(pdev); + pci_disable_msix(pdev); pci_release_regions(pdev); pci_disable_device(pdev); } -- 2.7.4