pci-epf-test/pci_endpoint_test: Add MSI-X support

Add MSI-X support and update driver documentation accordingly.

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
Gustavo Pimentel 2018-07-19 10:32:19 +02:00 committed by Lorenzo Pieralisi
parent 9133e394d5
commit c2e00e3108
10 changed files with 79 additions and 16 deletions

View file

@ -44,7 +44,7 @@ by the PCI controller driver.
* clear_bar: ops to reset the BAR * clear_bar: ops to reset the BAR
* alloc_addr_space: ops to allocate in PCI controller address space * alloc_addr_space: ops to allocate in PCI controller address space
* free_addr_space: ops to free the allocated address space * free_addr_space: ops to free the allocated address space
* raise_irq: ops to raise a legacy or MSI interrupt * raise_irq: ops to raise a legacy, MSI or MSI-X interrupt
* start: ops to start the PCI link * start: ops to start the PCI link
* stop: ops to stop the PCI link * stop: ops to stop the PCI link
@ -96,7 +96,7 @@ by the PCI endpoint function driver.
*) pci_epc_raise_irq() *) pci_epc_raise_irq()
The PCI endpoint function driver should use pci_epc_raise_irq() to raise The PCI endpoint function driver should use pci_epc_raise_irq() to raise
Legacy Interrupt or MSI Interrupt. Legacy Interrupt, MSI or MSI-X Interrupt.
*) pci_epc_mem_alloc_addr() *) pci_epc_mem_alloc_addr()

View file

@ -36,7 +36,7 @@ that the endpoint device must perform.
Bitfield Description: Bitfield Description:
Bit 0 : raise legacy IRQ Bit 0 : raise legacy IRQ
Bit 1 : raise MSI IRQ Bit 1 : raise MSI IRQ
Bit 2 : raise MSI-X IRQ (reserved for future implementation) Bit 2 : raise MSI-X IRQ
Bit 3 : read command (read data from RC buffer) Bit 3 : read command (read data from RC buffer)
Bit 4 : write command (write data to RC buffer) Bit 4 : write command (write data to RC buffer)
Bit 5 : copy command (copy data from one RC buffer to another Bit 5 : copy command (copy data from one RC buffer to another
@ -75,6 +75,7 @@ for the READ/WRITE/COPY and raise IRQ (Legacy/MSI) commands.
Possible types: Possible types:
- Legacy : 0 - Legacy : 0
- MSI : 1 - MSI : 1
- MSI-X : 2
*) PCI_ENDPOINT_TEST_IRQ_NUMBER *) PCI_ENDPOINT_TEST_IRQ_NUMBER
@ -83,3 +84,4 @@ This register contains the triggered ID interrupt.
Admissible values: Admissible values:
- Legacy : 0 - Legacy : 0
- MSI : [1 .. 32] - MSI : [1 .. 32]
- MSI-X : [1 .. 2048]

View file

@ -45,9 +45,9 @@ The PCI endpoint framework populates the directory with the following
configurable fields. configurable fields.
# ls functions/pci_epf_test/func1 # ls functions/pci_epf_test/func1
baseclass_code interrupt_pin revid subsys_vendor_id baseclass_code interrupt_pin progif_code subsys_id
cache_line_size msi_interrupts subclass_code vendorid cache_line_size msi_interrupts revid subsys_vendorid
deviceid progif_code subsys_id deviceid msix_interrupts subclass_code vendorid
The PCI endpoint function driver populates these entries with default values The PCI endpoint function driver populates these entries with default values
when the device is bound to the driver. The pci-epf-test driver populates when the device is bound to the driver. The pci-epf-test driver populates
@ -67,6 +67,7 @@ device, the following commands can be used.
# echo 0x104c > functions/pci_epf_test/func1/vendorid # echo 0x104c > functions/pci_epf_test/func1/vendorid
# echo 0xb500 > functions/pci_epf_test/func1/deviceid # echo 0xb500 > functions/pci_epf_test/func1/deviceid
# echo 16 > functions/pci_epf_test/func1/msi_interrupts # echo 16 > functions/pci_epf_test/func1/msi_interrupts
# echo 8 > functions/pci_epf_test/func1/msix_interrupts
1.5 Binding pci-epf-test Device to EP Controller 1.5 Binding pci-epf-test Device to EP Controller
@ -153,6 +154,21 @@ following commands.
MSI30: NOT OKAY MSI30: NOT OKAY
MSI31: NOT OKAY MSI31: NOT OKAY
MSI32: NOT OKAY MSI32: NOT OKAY
MSIX1: OKAY
MSIX2: OKAY
MSIX3: OKAY
MSIX4: OKAY
MSIX5: OKAY
MSIX6: OKAY
MSIX7: OKAY
MSIX8: OKAY
MSIX9: NOT OKAY
MSIX10: NOT OKAY
MSIX11: NOT OKAY
MSIX12: NOT OKAY
MSIX13: NOT OKAY
[...]
MSIX2048: NOT OKAY
Read Tests Read Tests

View file

@ -166,6 +166,7 @@ Code Seq#(hex) Include File Comments
'P' all linux/soundcard.h conflict! 'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict! 'P' 60-6F sound/sscape_ioctl.h conflict!
'P' 00-0F drivers/usb/class/usblp.c conflict! 'P' 00-0F drivers/usb/class/usblp.c conflict!
'P' 01-07 drivers/misc/pci_endpoint_test.c conflict!
'Q' all linux/soundcard.h 'Q' all linux/soundcard.h
'R' 00-1F linux/random.h conflict! 'R' 00-1F linux/random.h conflict!
'R' 01 linux/rfkill.h conflict! 'R' 01 linux/rfkill.h conflict!

View file

@ -10,6 +10,7 @@ The PCI driver for the test device performs the following tests
*) verifying addresses programmed in BAR *) verifying addresses programmed in BAR
*) raise legacy IRQ *) raise legacy IRQ
*) raise MSI IRQ *) raise MSI IRQ
*) raise MSI-X IRQ
*) read data *) read data
*) write data *) write data
*) copy data *) copy data
@ -25,6 +26,8 @@ ioctl
PCITEST_LEGACY_IRQ: Tests legacy IRQ PCITEST_LEGACY_IRQ: Tests legacy IRQ
PCITEST_MSI: Tests message signalled interrupts. The MSI number PCITEST_MSI: Tests message signalled interrupts. The MSI number
to be tested should be passed as argument. 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 PCITEST_WRITE: Perform write tests. The size of the buffer should be passed
as argument. as argument.
PCITEST_READ: Perform read tests. The size of the buffer should be passed PCITEST_READ: Perform read tests. The size of the buffer should be passed

View file

@ -39,13 +39,14 @@
#define IRQ_TYPE_LEGACY 0 #define IRQ_TYPE_LEGACY 0
#define IRQ_TYPE_MSI 1 #define IRQ_TYPE_MSI 1
#define IRQ_TYPE_MSIX 2
#define PCI_ENDPOINT_TEST_MAGIC 0x0 #define PCI_ENDPOINT_TEST_MAGIC 0x0
#define PCI_ENDPOINT_TEST_COMMAND 0x4 #define PCI_ENDPOINT_TEST_COMMAND 0x4
#define COMMAND_RAISE_LEGACY_IRQ BIT(0) #define COMMAND_RAISE_LEGACY_IRQ BIT(0)
#define COMMAND_RAISE_MSI_IRQ BIT(1) #define COMMAND_RAISE_MSI_IRQ BIT(1)
/* BIT(2) is reserved for raising MSI-X IRQ command */ #define COMMAND_RAISE_MSIX_IRQ BIT(2)
#define COMMAND_READ BIT(3) #define COMMAND_READ BIT(3)
#define COMMAND_WRITE BIT(4) #define COMMAND_WRITE BIT(4)
#define COMMAND_COPY BIT(5) #define COMMAND_COPY BIT(5)
@ -84,7 +85,7 @@ MODULE_PARM_DESC(no_msi, "Disable MSI interrupt in pci_endpoint_test");
static int irq_type = IRQ_TYPE_MSI; static int irq_type = IRQ_TYPE_MSI;
module_param(irq_type, int, 0444); module_param(irq_type, int, 0444);
MODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI)"); MODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI, 2 - MSI-X)");
enum pci_barno { enum pci_barno {
BAR_0, BAR_0,
@ -202,16 +203,18 @@ static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test)
} }
static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
u8 msi_num) u16 msi_num, bool msix)
{ {
u32 val; u32 val;
struct pci_dev *pdev = test->pdev; struct pci_dev *pdev = test->pdev;
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
IRQ_TYPE_MSI); msix == false ? IRQ_TYPE_MSI :
IRQ_TYPE_MSIX);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, msi_num); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, msi_num);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
COMMAND_RAISE_MSI_IRQ); msix == false ? COMMAND_RAISE_MSI_IRQ :
COMMAND_RAISE_MSIX_IRQ);
val = wait_for_completion_timeout(&test->irq_raised, val = wait_for_completion_timeout(&test->irq_raised,
msecs_to_jiffies(1000)); msecs_to_jiffies(1000));
if (!val) if (!val)
@ -456,7 +459,8 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
ret = pci_endpoint_test_legacy_irq(test); ret = pci_endpoint_test_legacy_irq(test);
break; break;
case PCITEST_MSI: case PCITEST_MSI:
ret = pci_endpoint_test_msi_irq(test, arg); case PCITEST_MSIX:
ret = pci_endpoint_test_msi_irq(test, arg, cmd == PCITEST_MSIX);
break; break;
case PCITEST_WRITE: case PCITEST_WRITE:
ret = pci_endpoint_test_write(test, arg); ret = pci_endpoint_test_write(test, arg);
@ -542,6 +546,12 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
dev_err(dev, "Failed to get MSI interrupts\n"); dev_err(dev, "Failed to get MSI interrupts\n");
test->num_irqs = irq; test->num_irqs = irq;
break; 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: default:
dev_err(dev, "Invalid IRQ type selected\n"); dev_err(dev, "Invalid IRQ type selected\n");
} }
@ -558,8 +568,9 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
pci_endpoint_test_irqhandler, pci_endpoint_test_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, test); IRQF_SHARED, DRV_MODULE_NAME, test);
if (err) if (err)
dev_err(dev, "failed to request IRQ %d for MSI %d\n", dev_err(dev, "Failed to request IRQ %d for MSI%s %d\n",
pci_irq_vector(pdev, i), i + 1); pci_irq_vector(pdev, i),
irq_type == IRQ_TYPE_MSIX ? "-X" : "", i + 1);
} }
for (bar = BAR_0; bar <= BAR_5; bar++) { for (bar = BAR_0; bar <= BAR_5; bar++) {
@ -625,6 +636,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
err_disable_msi: err_disable_msi:
pci_disable_msi(pdev); pci_disable_msi(pdev);
pci_disable_msix(pdev);
pci_release_regions(pdev); pci_release_regions(pdev);
err_disable_pdev: err_disable_pdev:
@ -656,6 +668,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
for (i = 0; i < test->num_irqs; i++) for (i = 0; i < test->num_irqs; i++)
devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), test); devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), test);
pci_disable_msi(pdev); pci_disable_msi(pdev);
pci_disable_msix(pdev);
pci_release_regions(pdev); pci_release_regions(pdev);
pci_disable_device(pdev); pci_disable_device(pdev);
} }

View file

@ -77,6 +77,7 @@ static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep)
dw_pcie_ep_reset_bar(pci, bar); dw_pcie_ep_reset_bar(pci, bar);
epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER; epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER;
epc->features |= EPC_FEATURE_MSIX_AVAILABLE;
} }
static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,

View file

@ -20,10 +20,11 @@
#define IRQ_TYPE_LEGACY 0 #define IRQ_TYPE_LEGACY 0
#define IRQ_TYPE_MSI 1 #define IRQ_TYPE_MSI 1
#define IRQ_TYPE_MSIX 2
#define COMMAND_RAISE_LEGACY_IRQ BIT(0) #define COMMAND_RAISE_LEGACY_IRQ BIT(0)
#define COMMAND_RAISE_MSI_IRQ BIT(1) #define COMMAND_RAISE_MSI_IRQ BIT(1)
/* BIT(2) is reserved for raising MSI-X IRQ command */ #define COMMAND_RAISE_MSIX_IRQ BIT(2)
#define COMMAND_READ BIT(3) #define COMMAND_READ BIT(3)
#define COMMAND_WRITE BIT(4) #define COMMAND_WRITE BIT(4)
#define COMMAND_COPY BIT(5) #define COMMAND_COPY BIT(5)
@ -47,6 +48,7 @@ struct pci_epf_test {
struct pci_epf *epf; struct pci_epf *epf;
enum pci_barno test_reg_bar; enum pci_barno test_reg_bar;
bool linkup_notifier; bool linkup_notifier;
bool msix_available;
struct delayed_work cmd_handler; struct delayed_work cmd_handler;
}; };
@ -266,6 +268,9 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, u8 irq_type,
case IRQ_TYPE_MSI: case IRQ_TYPE_MSI:
pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq); pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq);
break; break;
case IRQ_TYPE_MSIX:
pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSIX, irq);
break;
default: default:
dev_err(dev, "Failed to raise IRQ, unknown type\n"); dev_err(dev, "Failed to raise IRQ, unknown type\n");
break; break;
@ -292,7 +297,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
reg->command = 0; reg->command = 0;
reg->status = 0; reg->status = 0;
if (reg->irq_type > IRQ_TYPE_MSI) { if (reg->irq_type > IRQ_TYPE_MSIX) {
dev_err(dev, "Failed to detect IRQ type\n"); dev_err(dev, "Failed to detect IRQ type\n");
goto reset_handler; goto reset_handler;
} }
@ -346,6 +351,16 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
goto reset_handler; goto reset_handler;
} }
if (command & COMMAND_RAISE_MSIX_IRQ) {
count = pci_epc_get_msix(epc, epf->func_no);
if (reg->irq_number > count || count <= 0)
goto reset_handler;
reg->status = STATUS_IRQ_RAISED;
pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSIX,
reg->irq_number);
goto reset_handler;
}
reset_handler: reset_handler:
queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler, queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler,
msecs_to_jiffies(1)); msecs_to_jiffies(1));
@ -459,6 +474,8 @@ static int pci_epf_test_bind(struct pci_epf *epf)
else else
epf_test->linkup_notifier = true; epf_test->linkup_notifier = true;
epf_test->msix_available = epc->features & EPC_FEATURE_MSIX_AVAILABLE;
epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features); epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features);
ret = pci_epc_write_header(epc, epf->func_no, header); ret = pci_epc_write_header(epc, epf->func_no, header);
@ -481,6 +498,14 @@ static int pci_epf_test_bind(struct pci_epf *epf)
return ret; return ret;
} }
if (epf_test->msix_available) {
ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts);
if (ret) {
dev_err(dev, "MSI-X configuration failed\n");
return ret;
}
}
if (!epf_test->linkup_notifier) if (!epf_test->linkup_notifier)
queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work); queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work);

View file

@ -102,6 +102,7 @@ struct pci_epc {
#define EPC_FEATURE_NO_LINKUP_NOTIFIER BIT(0) #define EPC_FEATURE_NO_LINKUP_NOTIFIER BIT(0)
#define EPC_FEATURE_BAR_MASK (BIT(1) | BIT(2) | BIT(3)) #define EPC_FEATURE_BAR_MASK (BIT(1) | BIT(2) | BIT(3))
#define EPC_FEATURE_MSIX_AVAILABLE BIT(4)
#define EPC_FEATURE_SET_BAR(features, bar) \ #define EPC_FEATURE_SET_BAR(features, bar) \
(features |= (EPC_FEATURE_BAR_MASK & (bar << 1))) (features |= (EPC_FEATURE_BAR_MASK & (bar << 1)))
#define EPC_FEATURE_GET_BAR(features) \ #define EPC_FEATURE_GET_BAR(features) \

View file

@ -16,5 +16,6 @@
#define PCITEST_WRITE _IOW('P', 0x4, unsigned long) #define PCITEST_WRITE _IOW('P', 0x4, unsigned long)
#define PCITEST_READ _IOW('P', 0x5, unsigned long) #define PCITEST_READ _IOW('P', 0x5, unsigned long)
#define PCITEST_COPY _IOW('P', 0x6, unsigned long) #define PCITEST_COPY _IOW('P', 0x6, unsigned long)
#define PCITEST_MSIX _IOW('P', 0x7, int)
#endif /* __UAPI_LINUX_PCITEST_H */ #endif /* __UAPI_LINUX_PCITEST_H */