diff options
author | Gustavo Pimentel <gustavo.pimentel@synopsys.com> | 2018-07-19 04:32:20 -0400 |
---|---|---|
committer | Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | 2018-07-19 06:46:57 -0400 |
commit | e03327122e2c8e6ae4565ef5b3d3cbe4364546a1 (patch) | |
tree | 99dfa547dc2e84beac39eeea61192cc80ba46004 | |
parent | c2e00e31087e58f6c49b90b4702fc3df4fad6a83 (diff) |
pci_endpoint_test: Add 2 ioctl commands
Add MSI-X support and update driver documentation accordingly.
Add 2 new IOCTL commands:
- Allow to reconfigure driver IRQ type in runtime.
- Allow to retrieve current driver IRQ type configured.
Add IRQ type validation before executing the READ/WRITE/COPY tests.
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>
-rw-r--r-- | Documentation/ioctl/ioctl-number.txt | 2 | ||||
-rw-r--r-- | Documentation/misc-devices/pci-endpoint-test.txt | 3 | ||||
-rw-r--r-- | drivers/misc/pci_endpoint_test.c | 206 | ||||
-rw-r--r-- | include/uapi/linux/pcitest.h | 2 |
4 files changed, 165 insertions, 48 deletions
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 65259d459fd1..c15c4f3bdd82 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt | |||
@@ -166,7 +166,7 @@ Code Seq#(hex) Include File Comments | |||
166 | 'P' all linux/soundcard.h conflict! | 166 | 'P' all linux/soundcard.h conflict! |
167 | 'P' 60-6F sound/sscape_ioctl.h conflict! | 167 | 'P' 60-6F sound/sscape_ioctl.h conflict! |
168 | 'P' 00-0F drivers/usb/class/usblp.c conflict! | 168 | 'P' 00-0F drivers/usb/class/usblp.c conflict! |
169 | 'P' 01-07 drivers/misc/pci_endpoint_test.c conflict! | 169 | 'P' 01-09 drivers/misc/pci_endpoint_test.c conflict! |
170 | 'Q' all linux/soundcard.h | 170 | 'Q' all linux/soundcard.h |
171 | 'R' 00-1F linux/random.h conflict! | 171 | 'R' 00-1F linux/random.h conflict! |
172 | 'R' 01 linux/rfkill.h conflict! | 172 | 'R' 01 linux/rfkill.h conflict! |
diff --git a/Documentation/misc-devices/pci-endpoint-test.txt b/Documentation/misc-devices/pci-endpoint-test.txt index fdfa0f66d3d0..58ccca4416b1 100644 --- a/Documentation/misc-devices/pci-endpoint-test.txt +++ b/Documentation/misc-devices/pci-endpoint-test.txt | |||
@@ -28,6 +28,9 @@ ioctl | |||
28 | to be tested should be passed as argument. | 28 | to be tested should be passed as argument. |
29 | PCITEST_MSIX: Tests message signalled interrupts. The MSI-X number | 29 | PCITEST_MSIX: Tests message signalled interrupts. The MSI-X number |
30 | to be tested should be passed as argument. | 30 | to be tested should be passed as argument. |
31 | PCITEST_SET_IRQTYPE: Changes driver IRQ type configuration. The IRQ type | ||
32 | should be passed as argument (0: Legacy, 1:MSI, 2:MSI-X). | ||
33 | PCITEST_GET_IRQTYPE: Gets driver IRQ type configuration. | ||
31 | PCITEST_WRITE: Perform write tests. The size of the buffer should be passed | 34 | PCITEST_WRITE: Perform write tests. The size of the buffer should be passed |
32 | as argument. | 35 | as argument. |
33 | PCITEST_READ: Perform read tests. The size of the buffer should be passed | 36 | 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 f4fef108caff..896e2df9400f 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c | |||
@@ -37,6 +37,7 @@ | |||
37 | 37 | ||
38 | #define DRV_MODULE_NAME "pci-endpoint-test" | 38 | #define DRV_MODULE_NAME "pci-endpoint-test" |
39 | 39 | ||
40 | #define IRQ_TYPE_UNDEFINED -1 | ||
40 | #define IRQ_TYPE_LEGACY 0 | 41 | #define IRQ_TYPE_LEGACY 0 |
41 | #define IRQ_TYPE_MSI 1 | 42 | #define IRQ_TYPE_MSI 1 |
42 | #define IRQ_TYPE_MSIX 2 | 43 | #define IRQ_TYPE_MSIX 2 |
@@ -157,6 +158,100 @@ static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id) | |||
157 | return IRQ_HANDLED; | 158 | return IRQ_HANDLED; |
158 | } | 159 | } |
159 | 160 | ||
161 | static void pci_endpoint_test_free_irq_vectors(struct pci_endpoint_test *test) | ||
162 | { | ||
163 | struct pci_dev *pdev = test->pdev; | ||
164 | |||
165 | pci_free_irq_vectors(pdev); | ||
166 | } | ||
167 | |||
168 | static bool pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test, | ||
169 | int type) | ||
170 | { | ||
171 | int irq = -1; | ||
172 | struct pci_dev *pdev = test->pdev; | ||
173 | struct device *dev = &pdev->dev; | ||
174 | bool res = true; | ||
175 | |||
176 | switch (type) { | ||
177 | case IRQ_TYPE_LEGACY: | ||
178 | irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY); | ||
179 | if (irq < 0) | ||
180 | dev_err(dev, "Failed to get Legacy interrupt\n"); | ||
181 | break; | ||
182 | case IRQ_TYPE_MSI: | ||
183 | irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI); | ||
184 | if (irq < 0) | ||
185 | dev_err(dev, "Failed to get MSI interrupts\n"); | ||
186 | break; | ||
187 | case IRQ_TYPE_MSIX: | ||
188 | irq = pci_alloc_irq_vectors(pdev, 1, 2048, PCI_IRQ_MSIX); | ||
189 | if (irq < 0) | ||
190 | dev_err(dev, "Failed to get MSI-X interrupts\n"); | ||
191 | break; | ||
192 | default: | ||
193 | dev_err(dev, "Invalid IRQ type selected\n"); | ||
194 | } | ||
195 | |||
196 | if (irq < 0) { | ||
197 | irq = 0; | ||
198 | res = false; | ||
199 | } | ||
200 | test->num_irqs = irq; | ||
201 | |||
202 | return res; | ||
203 | } | ||
204 | |||
205 | static void pci_endpoint_test_release_irq(struct pci_endpoint_test *test) | ||
206 | { | ||
207 | int i; | ||
208 | struct pci_dev *pdev = test->pdev; | ||
209 | struct device *dev = &pdev->dev; | ||
210 | |||
211 | for (i = 0; i < test->num_irqs; i++) | ||
212 | devm_free_irq(dev, pci_irq_vector(pdev, i), test); | ||
213 | |||
214 | test->num_irqs = 0; | ||
215 | } | ||
216 | |||
217 | static bool pci_endpoint_test_request_irq(struct pci_endpoint_test *test) | ||
218 | { | ||
219 | int i; | ||
220 | int err; | ||
221 | struct pci_dev *pdev = test->pdev; | ||
222 | struct device *dev = &pdev->dev; | ||
223 | |||
224 | for (i = 0; i < test->num_irqs; i++) { | ||
225 | err = devm_request_irq(dev, pci_irq_vector(pdev, i), | ||
226 | pci_endpoint_test_irqhandler, | ||
227 | IRQF_SHARED, DRV_MODULE_NAME, test); | ||
228 | if (err) | ||
229 | goto fail; | ||
230 | } | ||
231 | |||
232 | return true; | ||
233 | |||
234 | fail: | ||
235 | switch (irq_type) { | ||
236 | case IRQ_TYPE_LEGACY: | ||
237 | dev_err(dev, "Failed to request IRQ %d for Legacy\n", | ||
238 | pci_irq_vector(pdev, i)); | ||
239 | break; | ||
240 | case IRQ_TYPE_MSI: | ||
241 | dev_err(dev, "Failed to request IRQ %d for MSI %d\n", | ||
242 | pci_irq_vector(pdev, i), | ||
243 | i + 1); | ||
244 | break; | ||
245 | case IRQ_TYPE_MSIX: | ||
246 | dev_err(dev, "Failed to request IRQ %d for MSI-X %d\n", | ||
247 | pci_irq_vector(pdev, i), | ||
248 | i + 1); | ||
249 | break; | ||
250 | } | ||
251 | |||
252 | return false; | ||
253 | } | ||
254 | |||
160 | static bool pci_endpoint_test_bar(struct pci_endpoint_test *test, | 255 | static bool pci_endpoint_test_bar(struct pci_endpoint_test *test, |
161 | enum pci_barno barno) | 256 | enum pci_barno barno) |
162 | { | 257 | { |
@@ -247,6 +342,11 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size) | |||
247 | if (size > SIZE_MAX - alignment) | 342 | if (size > SIZE_MAX - alignment) |
248 | goto err; | 343 | goto err; |
249 | 344 | ||
345 | if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) { | ||
346 | dev_err(dev, "Invalid IRQ type option\n"); | ||
347 | goto err; | ||
348 | } | ||
349 | |||
250 | orig_src_addr = dma_alloc_coherent(dev, size + alignment, | 350 | orig_src_addr = dma_alloc_coherent(dev, size + alignment, |
251 | &orig_src_phys_addr, GFP_KERNEL); | 351 | &orig_src_phys_addr, GFP_KERNEL); |
252 | if (!orig_src_addr) { | 352 | if (!orig_src_addr) { |
@@ -337,6 +437,11 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size) | |||
337 | if (size > SIZE_MAX - alignment) | 437 | if (size > SIZE_MAX - alignment) |
338 | goto err; | 438 | goto err; |
339 | 439 | ||
440 | if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) { | ||
441 | dev_err(dev, "Invalid IRQ type option\n"); | ||
442 | goto err; | ||
443 | } | ||
444 | |||
340 | orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr, | 445 | orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr, |
341 | GFP_KERNEL); | 446 | GFP_KERNEL); |
342 | if (!orig_addr) { | 447 | if (!orig_addr) { |
@@ -400,6 +505,11 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size) | |||
400 | if (size > SIZE_MAX - alignment) | 505 | if (size > SIZE_MAX - alignment) |
401 | goto err; | 506 | goto err; |
402 | 507 | ||
508 | if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) { | ||
509 | dev_err(dev, "Invalid IRQ type option\n"); | ||
510 | goto err; | ||
511 | } | ||
512 | |||
403 | orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr, | 513 | orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr, |
404 | GFP_KERNEL); | 514 | GFP_KERNEL); |
405 | if (!orig_addr) { | 515 | if (!orig_addr) { |
@@ -440,6 +550,38 @@ err: | |||
440 | return ret; | 550 | return ret; |
441 | } | 551 | } |
442 | 552 | ||
553 | static bool pci_endpoint_test_set_irq(struct pci_endpoint_test *test, | ||
554 | int req_irq_type) | ||
555 | { | ||
556 | struct pci_dev *pdev = test->pdev; | ||
557 | struct device *dev = &pdev->dev; | ||
558 | |||
559 | if (req_irq_type < IRQ_TYPE_LEGACY || req_irq_type > IRQ_TYPE_MSIX) { | ||
560 | dev_err(dev, "Invalid IRQ type option\n"); | ||
561 | return false; | ||
562 | } | ||
563 | |||
564 | if (irq_type == req_irq_type) | ||
565 | return true; | ||
566 | |||
567 | pci_endpoint_test_release_irq(test); | ||
568 | pci_endpoint_test_free_irq_vectors(test); | ||
569 | |||
570 | if (!pci_endpoint_test_alloc_irq_vectors(test, req_irq_type)) | ||
571 | goto err; | ||
572 | |||
573 | if (!pci_endpoint_test_request_irq(test)) | ||
574 | goto err; | ||
575 | |||
576 | irq_type = req_irq_type; | ||
577 | return true; | ||
578 | |||
579 | err: | ||
580 | pci_endpoint_test_free_irq_vectors(test); | ||
581 | irq_type = IRQ_TYPE_UNDEFINED; | ||
582 | return false; | ||
583 | } | ||
584 | |||
443 | static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, | 585 | static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, |
444 | unsigned long arg) | 586 | unsigned long arg) |
445 | { | 587 | { |
@@ -471,6 +613,12 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, | |||
471 | case PCITEST_COPY: | 613 | case PCITEST_COPY: |
472 | ret = pci_endpoint_test_copy(test, arg); | 614 | ret = pci_endpoint_test_copy(test, arg); |
473 | break; | 615 | break; |
616 | case PCITEST_SET_IRQTYPE: | ||
617 | ret = pci_endpoint_test_set_irq(test, arg); | ||
618 | break; | ||
619 | case PCITEST_GET_IRQTYPE: | ||
620 | ret = irq_type; | ||
621 | break; | ||
474 | } | 622 | } |
475 | 623 | ||
476 | ret: | 624 | ret: |
@@ -486,9 +634,7 @@ static const struct file_operations pci_endpoint_test_fops = { | |||
486 | static int pci_endpoint_test_probe(struct pci_dev *pdev, | 634 | static int pci_endpoint_test_probe(struct pci_dev *pdev, |
487 | const struct pci_device_id *ent) | 635 | const struct pci_device_id *ent) |
488 | { | 636 | { |
489 | int i; | ||
490 | int err; | 637 | int err; |
491 | int irq = 0; | ||
492 | int id; | 638 | int id; |
493 | char name[20]; | 639 | char name[20]; |
494 | enum pci_barno bar; | 640 | enum pci_barno bar; |
@@ -537,41 +683,11 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, | |||
537 | 683 | ||
538 | pci_set_master(pdev); | 684 | pci_set_master(pdev); |
539 | 685 | ||
540 | switch (irq_type) { | 686 | if (!pci_endpoint_test_alloc_irq_vectors(test, irq_type)) |
541 | case IRQ_TYPE_LEGACY: | 687 | goto err_disable_irq; |
542 | break; | ||
543 | case IRQ_TYPE_MSI: | ||
544 | irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI); | ||
545 | if (irq < 0) | ||
546 | dev_err(dev, "Failed to get MSI interrupts\n"); | ||
547 | test->num_irqs = irq; | ||
548 | break; | ||
549 | case IRQ_TYPE_MSIX: | ||
550 | irq = pci_alloc_irq_vectors(pdev, 1, 2048, PCI_IRQ_MSIX); | ||
551 | if (irq < 0) | ||
552 | dev_err(dev, "Failed to get MSI-X interrupts\n"); | ||
553 | test->num_irqs = irq; | ||
554 | break; | ||
555 | default: | ||
556 | dev_err(dev, "Invalid IRQ type selected\n"); | ||
557 | } | ||
558 | 688 | ||
559 | err = devm_request_irq(dev, pdev->irq, pci_endpoint_test_irqhandler, | 689 | if (!pci_endpoint_test_request_irq(test)) |
560 | IRQF_SHARED, DRV_MODULE_NAME, test); | 690 | goto err_disable_irq; |
561 | if (err) { | ||
562 | dev_err(dev, "Failed to request IRQ %d\n", pdev->irq); | ||
563 | goto err_disable_msi; | ||
564 | } | ||
565 | |||
566 | for (i = 1; i < irq; i++) { | ||
567 | err = devm_request_irq(dev, pci_irq_vector(pdev, i), | ||
568 | pci_endpoint_test_irqhandler, | ||
569 | IRQF_SHARED, DRV_MODULE_NAME, test); | ||
570 | if (err) | ||
571 | dev_err(dev, "Failed to request IRQ %d for MSI%s %d\n", | ||
572 | pci_irq_vector(pdev, i), | ||
573 | irq_type == IRQ_TYPE_MSIX ? "-X" : "", i + 1); | ||
574 | } | ||
575 | 691 | ||
576 | for (bar = BAR_0; bar <= BAR_5; bar++) { | 692 | for (bar = BAR_0; bar <= BAR_5; bar++) { |
577 | if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { | 693 | if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { |
@@ -630,13 +746,10 @@ err_iounmap: | |||
630 | if (test->bar[bar]) | 746 | if (test->bar[bar]) |
631 | pci_iounmap(pdev, test->bar[bar]); | 747 | pci_iounmap(pdev, test->bar[bar]); |
632 | } | 748 | } |
749 | pci_endpoint_test_release_irq(test); | ||
633 | 750 | ||
634 | for (i = 0; i < irq; i++) | 751 | err_disable_irq: |
635 | devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), test); | 752 | pci_endpoint_test_free_irq_vectors(test); |
636 | |||
637 | err_disable_msi: | ||
638 | pci_disable_msi(pdev); | ||
639 | pci_disable_msix(pdev); | ||
640 | pci_release_regions(pdev); | 753 | pci_release_regions(pdev); |
641 | 754 | ||
642 | err_disable_pdev: | 755 | err_disable_pdev: |
@@ -648,7 +761,6 @@ err_disable_pdev: | |||
648 | static void pci_endpoint_test_remove(struct pci_dev *pdev) | 761 | static void pci_endpoint_test_remove(struct pci_dev *pdev) |
649 | { | 762 | { |
650 | int id; | 763 | int id; |
651 | int i; | ||
652 | enum pci_barno bar; | 764 | enum pci_barno bar; |
653 | struct pci_endpoint_test *test = pci_get_drvdata(pdev); | 765 | struct pci_endpoint_test *test = pci_get_drvdata(pdev); |
654 | struct miscdevice *misc_device = &test->miscdev; | 766 | struct miscdevice *misc_device = &test->miscdev; |
@@ -665,10 +777,10 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) | |||
665 | if (test->bar[bar]) | 777 | if (test->bar[bar]) |
666 | pci_iounmap(pdev, test->bar[bar]); | 778 | pci_iounmap(pdev, test->bar[bar]); |
667 | } | 779 | } |
668 | for (i = 0; i < test->num_irqs; i++) | 780 | |
669 | devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), test); | 781 | pci_endpoint_test_release_irq(test); |
670 | pci_disable_msi(pdev); | 782 | pci_endpoint_test_free_irq_vectors(test); |
671 | pci_disable_msix(pdev); | 783 | |
672 | pci_release_regions(pdev); | 784 | pci_release_regions(pdev); |
673 | pci_disable_device(pdev); | 785 | pci_disable_device(pdev); |
674 | } | 786 | } |
diff --git a/include/uapi/linux/pcitest.h b/include/uapi/linux/pcitest.h index d746fb159dcd..cbf422e56696 100644 --- a/include/uapi/linux/pcitest.h +++ b/include/uapi/linux/pcitest.h | |||
@@ -17,5 +17,7 @@ | |||
17 | #define PCITEST_READ _IOW('P', 0x5, unsigned long) | 17 | #define PCITEST_READ _IOW('P', 0x5, unsigned long) |
18 | #define PCITEST_COPY _IOW('P', 0x6, unsigned long) | 18 | #define PCITEST_COPY _IOW('P', 0x6, unsigned long) |
19 | #define PCITEST_MSIX _IOW('P', 0x7, int) | 19 | #define PCITEST_MSIX _IOW('P', 0x7, int) |
20 | #define PCITEST_SET_IRQTYPE _IOW('P', 0x8, int) | ||
21 | #define PCITEST_GET_IRQTYPE _IO('P', 0x9) | ||
20 | 22 | ||
21 | #endif /* __UAPI_LINUX_PCITEST_H */ | 23 | #endif /* __UAPI_LINUX_PCITEST_H */ |