162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/**
362306a36Sopenharmony_ci * Userspace PCI Endpoint Test Module
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2017 Texas Instruments
662306a36Sopenharmony_ci * Author: Kishon Vijay Abraham I <kishon@ti.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <errno.h>
1062306a36Sopenharmony_ci#include <fcntl.h>
1162306a36Sopenharmony_ci#include <stdbool.h>
1262306a36Sopenharmony_ci#include <stdio.h>
1362306a36Sopenharmony_ci#include <stdlib.h>
1462306a36Sopenharmony_ci#include <sys/ioctl.h>
1562306a36Sopenharmony_ci#include <unistd.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/pcitest.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define BILLION 1E9
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic char *result[] = { "NOT OKAY", "OKAY" };
2262306a36Sopenharmony_cistatic char *irq[] = { "LEGACY", "MSI", "MSI-X" };
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct pci_test {
2562306a36Sopenharmony_ci	char		*device;
2662306a36Sopenharmony_ci	char		barnum;
2762306a36Sopenharmony_ci	bool		legacyirq;
2862306a36Sopenharmony_ci	unsigned int	msinum;
2962306a36Sopenharmony_ci	unsigned int	msixnum;
3062306a36Sopenharmony_ci	int		irqtype;
3162306a36Sopenharmony_ci	bool		set_irqtype;
3262306a36Sopenharmony_ci	bool		get_irqtype;
3362306a36Sopenharmony_ci	bool		clear_irq;
3462306a36Sopenharmony_ci	bool		read;
3562306a36Sopenharmony_ci	bool		write;
3662306a36Sopenharmony_ci	bool		copy;
3762306a36Sopenharmony_ci	unsigned long	size;
3862306a36Sopenharmony_ci	bool		use_dma;
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int run_test(struct pci_test *test)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct pci_endpoint_test_xfer_param param = {};
4462306a36Sopenharmony_ci	int ret = -EINVAL;
4562306a36Sopenharmony_ci	int fd;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	fd = open(test->device, O_RDWR);
4862306a36Sopenharmony_ci	if (fd < 0) {
4962306a36Sopenharmony_ci		perror("can't open PCI Endpoint Test device");
5062306a36Sopenharmony_ci		return -ENODEV;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (test->barnum >= 0 && test->barnum <= 5) {
5462306a36Sopenharmony_ci		ret = ioctl(fd, PCITEST_BAR, test->barnum);
5562306a36Sopenharmony_ci		fprintf(stdout, "BAR%d:\t\t", test->barnum);
5662306a36Sopenharmony_ci		if (ret < 0)
5762306a36Sopenharmony_ci			fprintf(stdout, "TEST FAILED\n");
5862306a36Sopenharmony_ci		else
5962306a36Sopenharmony_ci			fprintf(stdout, "%s\n", result[ret]);
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (test->set_irqtype) {
6362306a36Sopenharmony_ci		ret = ioctl(fd, PCITEST_SET_IRQTYPE, test->irqtype);
6462306a36Sopenharmony_ci		fprintf(stdout, "SET IRQ TYPE TO %s:\t\t", irq[test->irqtype]);
6562306a36Sopenharmony_ci		if (ret < 0)
6662306a36Sopenharmony_ci			fprintf(stdout, "FAILED\n");
6762306a36Sopenharmony_ci		else
6862306a36Sopenharmony_ci			fprintf(stdout, "%s\n", result[ret]);
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (test->get_irqtype) {
7262306a36Sopenharmony_ci		ret = ioctl(fd, PCITEST_GET_IRQTYPE);
7362306a36Sopenharmony_ci		fprintf(stdout, "GET IRQ TYPE:\t\t");
7462306a36Sopenharmony_ci		if (ret < 0)
7562306a36Sopenharmony_ci			fprintf(stdout, "FAILED\n");
7662306a36Sopenharmony_ci		else
7762306a36Sopenharmony_ci			fprintf(stdout, "%s\n", irq[ret]);
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (test->clear_irq) {
8162306a36Sopenharmony_ci		ret = ioctl(fd, PCITEST_CLEAR_IRQ);
8262306a36Sopenharmony_ci		fprintf(stdout, "CLEAR IRQ:\t\t");
8362306a36Sopenharmony_ci		if (ret < 0)
8462306a36Sopenharmony_ci			fprintf(stdout, "FAILED\n");
8562306a36Sopenharmony_ci		else
8662306a36Sopenharmony_ci			fprintf(stdout, "%s\n", result[ret]);
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (test->legacyirq) {
9062306a36Sopenharmony_ci		ret = ioctl(fd, PCITEST_LEGACY_IRQ, 0);
9162306a36Sopenharmony_ci		fprintf(stdout, "LEGACY IRQ:\t");
9262306a36Sopenharmony_ci		if (ret < 0)
9362306a36Sopenharmony_ci			fprintf(stdout, "TEST FAILED\n");
9462306a36Sopenharmony_ci		else
9562306a36Sopenharmony_ci			fprintf(stdout, "%s\n", result[ret]);
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (test->msinum > 0 && test->msinum <= 32) {
9962306a36Sopenharmony_ci		ret = ioctl(fd, PCITEST_MSI, test->msinum);
10062306a36Sopenharmony_ci		fprintf(stdout, "MSI%d:\t\t", test->msinum);
10162306a36Sopenharmony_ci		if (ret < 0)
10262306a36Sopenharmony_ci			fprintf(stdout, "TEST FAILED\n");
10362306a36Sopenharmony_ci		else
10462306a36Sopenharmony_ci			fprintf(stdout, "%s\n", result[ret]);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (test->msixnum > 0 && test->msixnum <= 2048) {
10862306a36Sopenharmony_ci		ret = ioctl(fd, PCITEST_MSIX, test->msixnum);
10962306a36Sopenharmony_ci		fprintf(stdout, "MSI-X%d:\t\t", test->msixnum);
11062306a36Sopenharmony_ci		if (ret < 0)
11162306a36Sopenharmony_ci			fprintf(stdout, "TEST FAILED\n");
11262306a36Sopenharmony_ci		else
11362306a36Sopenharmony_ci			fprintf(stdout, "%s\n", result[ret]);
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (test->write) {
11762306a36Sopenharmony_ci		param.size = test->size;
11862306a36Sopenharmony_ci		if (test->use_dma)
11962306a36Sopenharmony_ci			param.flags = PCITEST_FLAGS_USE_DMA;
12062306a36Sopenharmony_ci		ret = ioctl(fd, PCITEST_WRITE, &param);
12162306a36Sopenharmony_ci		fprintf(stdout, "WRITE (%7ld bytes):\t\t", test->size);
12262306a36Sopenharmony_ci		if (ret < 0)
12362306a36Sopenharmony_ci			fprintf(stdout, "TEST FAILED\n");
12462306a36Sopenharmony_ci		else
12562306a36Sopenharmony_ci			fprintf(stdout, "%s\n", result[ret]);
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (test->read) {
12962306a36Sopenharmony_ci		param.size = test->size;
13062306a36Sopenharmony_ci		if (test->use_dma)
13162306a36Sopenharmony_ci			param.flags = PCITEST_FLAGS_USE_DMA;
13262306a36Sopenharmony_ci		ret = ioctl(fd, PCITEST_READ, &param);
13362306a36Sopenharmony_ci		fprintf(stdout, "READ (%7ld bytes):\t\t", test->size);
13462306a36Sopenharmony_ci		if (ret < 0)
13562306a36Sopenharmony_ci			fprintf(stdout, "TEST FAILED\n");
13662306a36Sopenharmony_ci		else
13762306a36Sopenharmony_ci			fprintf(stdout, "%s\n", result[ret]);
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (test->copy) {
14162306a36Sopenharmony_ci		param.size = test->size;
14262306a36Sopenharmony_ci		if (test->use_dma)
14362306a36Sopenharmony_ci			param.flags = PCITEST_FLAGS_USE_DMA;
14462306a36Sopenharmony_ci		ret = ioctl(fd, PCITEST_COPY, &param);
14562306a36Sopenharmony_ci		fprintf(stdout, "COPY (%7ld bytes):\t\t", test->size);
14662306a36Sopenharmony_ci		if (ret < 0)
14762306a36Sopenharmony_ci			fprintf(stdout, "TEST FAILED\n");
14862306a36Sopenharmony_ci		else
14962306a36Sopenharmony_ci			fprintf(stdout, "%s\n", result[ret]);
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	fflush(stdout);
15362306a36Sopenharmony_ci	close(fd);
15462306a36Sopenharmony_ci	return (ret < 0) ? ret : 1 - ret; /* return 0 if test succeeded */
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ciint main(int argc, char **argv)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	int c;
16062306a36Sopenharmony_ci	struct pci_test *test;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	test = calloc(1, sizeof(*test));
16362306a36Sopenharmony_ci	if (!test) {
16462306a36Sopenharmony_ci		perror("Fail to allocate memory for pci_test\n");
16562306a36Sopenharmony_ci		return -ENOMEM;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* since '0' is a valid BAR number, initialize it to -1 */
16962306a36Sopenharmony_ci	test->barnum = -1;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* set default size as 100KB */
17262306a36Sopenharmony_ci	test->size = 0x19000;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* set default endpoint device */
17562306a36Sopenharmony_ci	test->device = "/dev/pci-endpoint-test.0";
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	while ((c = getopt(argc, argv, "D:b:m:x:i:deIlhrwcs:")) != EOF)
17862306a36Sopenharmony_ci	switch (c) {
17962306a36Sopenharmony_ci	case 'D':
18062306a36Sopenharmony_ci		test->device = optarg;
18162306a36Sopenharmony_ci		continue;
18262306a36Sopenharmony_ci	case 'b':
18362306a36Sopenharmony_ci		test->barnum = atoi(optarg);
18462306a36Sopenharmony_ci		if (test->barnum < 0 || test->barnum > 5)
18562306a36Sopenharmony_ci			goto usage;
18662306a36Sopenharmony_ci		continue;
18762306a36Sopenharmony_ci	case 'l':
18862306a36Sopenharmony_ci		test->legacyirq = true;
18962306a36Sopenharmony_ci		continue;
19062306a36Sopenharmony_ci	case 'm':
19162306a36Sopenharmony_ci		test->msinum = atoi(optarg);
19262306a36Sopenharmony_ci		if (test->msinum < 1 || test->msinum > 32)
19362306a36Sopenharmony_ci			goto usage;
19462306a36Sopenharmony_ci		continue;
19562306a36Sopenharmony_ci	case 'x':
19662306a36Sopenharmony_ci		test->msixnum = atoi(optarg);
19762306a36Sopenharmony_ci		if (test->msixnum < 1 || test->msixnum > 2048)
19862306a36Sopenharmony_ci			goto usage;
19962306a36Sopenharmony_ci		continue;
20062306a36Sopenharmony_ci	case 'i':
20162306a36Sopenharmony_ci		test->irqtype = atoi(optarg);
20262306a36Sopenharmony_ci		if (test->irqtype < 0 || test->irqtype > 2)
20362306a36Sopenharmony_ci			goto usage;
20462306a36Sopenharmony_ci		test->set_irqtype = true;
20562306a36Sopenharmony_ci		continue;
20662306a36Sopenharmony_ci	case 'I':
20762306a36Sopenharmony_ci		test->get_irqtype = true;
20862306a36Sopenharmony_ci		continue;
20962306a36Sopenharmony_ci	case 'r':
21062306a36Sopenharmony_ci		test->read = true;
21162306a36Sopenharmony_ci		continue;
21262306a36Sopenharmony_ci	case 'w':
21362306a36Sopenharmony_ci		test->write = true;
21462306a36Sopenharmony_ci		continue;
21562306a36Sopenharmony_ci	case 'c':
21662306a36Sopenharmony_ci		test->copy = true;
21762306a36Sopenharmony_ci		continue;
21862306a36Sopenharmony_ci	case 'e':
21962306a36Sopenharmony_ci		test->clear_irq = true;
22062306a36Sopenharmony_ci		continue;
22162306a36Sopenharmony_ci	case 's':
22262306a36Sopenharmony_ci		test->size = strtoul(optarg, NULL, 0);
22362306a36Sopenharmony_ci		continue;
22462306a36Sopenharmony_ci	case 'd':
22562306a36Sopenharmony_ci		test->use_dma = true;
22662306a36Sopenharmony_ci		continue;
22762306a36Sopenharmony_ci	case 'h':
22862306a36Sopenharmony_ci	default:
22962306a36Sopenharmony_ciusage:
23062306a36Sopenharmony_ci		fprintf(stderr,
23162306a36Sopenharmony_ci			"usage: %s [options]\n"
23262306a36Sopenharmony_ci			"Options:\n"
23362306a36Sopenharmony_ci			"\t-D <dev>		PCI endpoint test device {default: /dev/pci-endpoint-test.0}\n"
23462306a36Sopenharmony_ci			"\t-b <bar num>		BAR test (bar number between 0..5)\n"
23562306a36Sopenharmony_ci			"\t-m <msi num>		MSI test (msi number between 1..32)\n"
23662306a36Sopenharmony_ci			"\t-x <msix num>	\tMSI-X test (msix number between 1..2048)\n"
23762306a36Sopenharmony_ci			"\t-i <irq type>	\tSet IRQ type (0 - Legacy, 1 - MSI, 2 - MSI-X)\n"
23862306a36Sopenharmony_ci			"\t-e			Clear IRQ\n"
23962306a36Sopenharmony_ci			"\t-I			Get current IRQ type configured\n"
24062306a36Sopenharmony_ci			"\t-d			Use DMA\n"
24162306a36Sopenharmony_ci			"\t-l			Legacy IRQ test\n"
24262306a36Sopenharmony_ci			"\t-r			Read buffer test\n"
24362306a36Sopenharmony_ci			"\t-w			Write buffer test\n"
24462306a36Sopenharmony_ci			"\t-c			Copy buffer test\n"
24562306a36Sopenharmony_ci			"\t-s <size>		Size of buffer {default: 100KB}\n"
24662306a36Sopenharmony_ci			"\t-h			Print this help message\n",
24762306a36Sopenharmony_ci			argv[0]);
24862306a36Sopenharmony_ci		return -EINVAL;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return run_test(test);
25262306a36Sopenharmony_ci}
253