162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Host side test driver to test endpoint functionality
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 <linux/crc32.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/irq.h>
1562306a36Sopenharmony_ci#include <linux/miscdevice.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/mutex.h>
1862306a36Sopenharmony_ci#include <linux/random.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/uaccess.h>
2162306a36Sopenharmony_ci#include <linux/pci.h>
2262306a36Sopenharmony_ci#include <linux/pci_ids.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/pci_regs.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <uapi/linux/pcitest.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define DRV_MODULE_NAME				"pci-endpoint-test"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define IRQ_TYPE_UNDEFINED			-1
3162306a36Sopenharmony_ci#define IRQ_TYPE_LEGACY				0
3262306a36Sopenharmony_ci#define IRQ_TYPE_MSI				1
3362306a36Sopenharmony_ci#define IRQ_TYPE_MSIX				2
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define PCI_ENDPOINT_TEST_MAGIC			0x0
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define PCI_ENDPOINT_TEST_COMMAND		0x4
3862306a36Sopenharmony_ci#define COMMAND_RAISE_LEGACY_IRQ		BIT(0)
3962306a36Sopenharmony_ci#define COMMAND_RAISE_MSI_IRQ			BIT(1)
4062306a36Sopenharmony_ci#define COMMAND_RAISE_MSIX_IRQ			BIT(2)
4162306a36Sopenharmony_ci#define COMMAND_READ				BIT(3)
4262306a36Sopenharmony_ci#define COMMAND_WRITE				BIT(4)
4362306a36Sopenharmony_ci#define COMMAND_COPY				BIT(5)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define PCI_ENDPOINT_TEST_STATUS		0x8
4662306a36Sopenharmony_ci#define STATUS_READ_SUCCESS			BIT(0)
4762306a36Sopenharmony_ci#define STATUS_READ_FAIL			BIT(1)
4862306a36Sopenharmony_ci#define STATUS_WRITE_SUCCESS			BIT(2)
4962306a36Sopenharmony_ci#define STATUS_WRITE_FAIL			BIT(3)
5062306a36Sopenharmony_ci#define STATUS_COPY_SUCCESS			BIT(4)
5162306a36Sopenharmony_ci#define STATUS_COPY_FAIL			BIT(5)
5262306a36Sopenharmony_ci#define STATUS_IRQ_RAISED			BIT(6)
5362306a36Sopenharmony_ci#define STATUS_SRC_ADDR_INVALID			BIT(7)
5462306a36Sopenharmony_ci#define STATUS_DST_ADDR_INVALID			BIT(8)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR	0x0c
5762306a36Sopenharmony_ci#define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR	0x10
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define PCI_ENDPOINT_TEST_LOWER_DST_ADDR	0x14
6062306a36Sopenharmony_ci#define PCI_ENDPOINT_TEST_UPPER_DST_ADDR	0x18
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define PCI_ENDPOINT_TEST_SIZE			0x1c
6362306a36Sopenharmony_ci#define PCI_ENDPOINT_TEST_CHECKSUM		0x20
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define PCI_ENDPOINT_TEST_IRQ_TYPE		0x24
6662306a36Sopenharmony_ci#define PCI_ENDPOINT_TEST_IRQ_NUMBER		0x28
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define PCI_ENDPOINT_TEST_FLAGS			0x2c
6962306a36Sopenharmony_ci#define FLAG_USE_DMA				BIT(0)
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define PCI_DEVICE_ID_TI_AM654			0xb00c
7262306a36Sopenharmony_ci#define PCI_DEVICE_ID_TI_J7200			0xb00f
7362306a36Sopenharmony_ci#define PCI_DEVICE_ID_TI_AM64			0xb010
7462306a36Sopenharmony_ci#define PCI_DEVICE_ID_TI_J721S2		0xb013
7562306a36Sopenharmony_ci#define PCI_DEVICE_ID_LS1088A			0x80c0
7662306a36Sopenharmony_ci#define PCI_DEVICE_ID_IMX8			0x0808
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#define is_am654_pci_dev(pdev)		\
7962306a36Sopenharmony_ci		((pdev)->device == PCI_DEVICE_ID_TI_AM654)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define PCI_DEVICE_ID_RENESAS_R8A774A1		0x0028
8262306a36Sopenharmony_ci#define PCI_DEVICE_ID_RENESAS_R8A774B1		0x002b
8362306a36Sopenharmony_ci#define PCI_DEVICE_ID_RENESAS_R8A774C0		0x002d
8462306a36Sopenharmony_ci#define PCI_DEVICE_ID_RENESAS_R8A774E1		0x0025
8562306a36Sopenharmony_ci#define PCI_DEVICE_ID_RENESAS_R8A779F0		0x0031
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic DEFINE_IDA(pci_endpoint_test_ida);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \
9062306a36Sopenharmony_ci					    miscdev)
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic bool no_msi;
9362306a36Sopenharmony_cimodule_param(no_msi, bool, 0444);
9462306a36Sopenharmony_ciMODULE_PARM_DESC(no_msi, "Disable MSI interrupt in pci_endpoint_test");
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int irq_type = IRQ_TYPE_MSI;
9762306a36Sopenharmony_cimodule_param(irq_type, int, 0444);
9862306a36Sopenharmony_ciMODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI, 2 - MSI-X)");
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cienum pci_barno {
10162306a36Sopenharmony_ci	BAR_0,
10262306a36Sopenharmony_ci	BAR_1,
10362306a36Sopenharmony_ci	BAR_2,
10462306a36Sopenharmony_ci	BAR_3,
10562306a36Sopenharmony_ci	BAR_4,
10662306a36Sopenharmony_ci	BAR_5,
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistruct pci_endpoint_test {
11062306a36Sopenharmony_ci	struct pci_dev	*pdev;
11162306a36Sopenharmony_ci	void __iomem	*base;
11262306a36Sopenharmony_ci	void __iomem	*bar[PCI_STD_NUM_BARS];
11362306a36Sopenharmony_ci	struct completion irq_raised;
11462306a36Sopenharmony_ci	int		last_irq;
11562306a36Sopenharmony_ci	int		num_irqs;
11662306a36Sopenharmony_ci	int		irq_type;
11762306a36Sopenharmony_ci	/* mutex to protect the ioctls */
11862306a36Sopenharmony_ci	struct mutex	mutex;
11962306a36Sopenharmony_ci	struct miscdevice miscdev;
12062306a36Sopenharmony_ci	enum pci_barno test_reg_bar;
12162306a36Sopenharmony_ci	size_t alignment;
12262306a36Sopenharmony_ci	const char *name;
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistruct pci_endpoint_test_data {
12662306a36Sopenharmony_ci	enum pci_barno test_reg_bar;
12762306a36Sopenharmony_ci	size_t alignment;
12862306a36Sopenharmony_ci	int irq_type;
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test,
13262306a36Sopenharmony_ci					  u32 offset)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	return readl(test->base + offset);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic inline void pci_endpoint_test_writel(struct pci_endpoint_test *test,
13862306a36Sopenharmony_ci					    u32 offset, u32 value)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	writel(value, test->base + offset);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic inline u32 pci_endpoint_test_bar_readl(struct pci_endpoint_test *test,
14462306a36Sopenharmony_ci					      int bar, int offset)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	return readl(test->bar[bar] + offset);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic inline void pci_endpoint_test_bar_writel(struct pci_endpoint_test *test,
15062306a36Sopenharmony_ci						int bar, u32 offset, u32 value)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	writel(value, test->bar[bar] + offset);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct pci_endpoint_test *test = dev_id;
15862306a36Sopenharmony_ci	u32 reg;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
16162306a36Sopenharmony_ci	if (reg & STATUS_IRQ_RAISED) {
16262306a36Sopenharmony_ci		test->last_irq = irq;
16362306a36Sopenharmony_ci		complete(&test->irq_raised);
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return IRQ_HANDLED;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic void pci_endpoint_test_free_irq_vectors(struct pci_endpoint_test *test)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct pci_dev *pdev = test->pdev;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	pci_free_irq_vectors(pdev);
17462306a36Sopenharmony_ci	test->irq_type = IRQ_TYPE_UNDEFINED;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic bool pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test,
17862306a36Sopenharmony_ci						int type)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	int irq = -1;
18162306a36Sopenharmony_ci	struct pci_dev *pdev = test->pdev;
18262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
18362306a36Sopenharmony_ci	bool res = true;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	switch (type) {
18662306a36Sopenharmony_ci	case IRQ_TYPE_LEGACY:
18762306a36Sopenharmony_ci		irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY);
18862306a36Sopenharmony_ci		if (irq < 0)
18962306a36Sopenharmony_ci			dev_err(dev, "Failed to get Legacy interrupt\n");
19062306a36Sopenharmony_ci		break;
19162306a36Sopenharmony_ci	case IRQ_TYPE_MSI:
19262306a36Sopenharmony_ci		irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
19362306a36Sopenharmony_ci		if (irq < 0)
19462306a36Sopenharmony_ci			dev_err(dev, "Failed to get MSI interrupts\n");
19562306a36Sopenharmony_ci		break;
19662306a36Sopenharmony_ci	case IRQ_TYPE_MSIX:
19762306a36Sopenharmony_ci		irq = pci_alloc_irq_vectors(pdev, 1, 2048, PCI_IRQ_MSIX);
19862306a36Sopenharmony_ci		if (irq < 0)
19962306a36Sopenharmony_ci			dev_err(dev, "Failed to get MSI-X interrupts\n");
20062306a36Sopenharmony_ci		break;
20162306a36Sopenharmony_ci	default:
20262306a36Sopenharmony_ci		dev_err(dev, "Invalid IRQ type selected\n");
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (irq < 0) {
20662306a36Sopenharmony_ci		irq = 0;
20762306a36Sopenharmony_ci		res = false;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	test->irq_type = type;
21162306a36Sopenharmony_ci	test->num_irqs = irq;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	return res;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic void pci_endpoint_test_release_irq(struct pci_endpoint_test *test)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	int i;
21962306a36Sopenharmony_ci	struct pci_dev *pdev = test->pdev;
22062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	for (i = 0; i < test->num_irqs; i++)
22362306a36Sopenharmony_ci		devm_free_irq(dev, pci_irq_vector(pdev, i), test);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	test->num_irqs = 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic bool pci_endpoint_test_request_irq(struct pci_endpoint_test *test)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	int i;
23162306a36Sopenharmony_ci	int err;
23262306a36Sopenharmony_ci	struct pci_dev *pdev = test->pdev;
23362306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	for (i = 0; i < test->num_irqs; i++) {
23662306a36Sopenharmony_ci		err = devm_request_irq(dev, pci_irq_vector(pdev, i),
23762306a36Sopenharmony_ci				       pci_endpoint_test_irqhandler,
23862306a36Sopenharmony_ci				       IRQF_SHARED, test->name, test);
23962306a36Sopenharmony_ci		if (err)
24062306a36Sopenharmony_ci			goto fail;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return true;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cifail:
24662306a36Sopenharmony_ci	switch (irq_type) {
24762306a36Sopenharmony_ci	case IRQ_TYPE_LEGACY:
24862306a36Sopenharmony_ci		dev_err(dev, "Failed to request IRQ %d for Legacy\n",
24962306a36Sopenharmony_ci			pci_irq_vector(pdev, i));
25062306a36Sopenharmony_ci		break;
25162306a36Sopenharmony_ci	case IRQ_TYPE_MSI:
25262306a36Sopenharmony_ci		dev_err(dev, "Failed to request IRQ %d for MSI %d\n",
25362306a36Sopenharmony_ci			pci_irq_vector(pdev, i),
25462306a36Sopenharmony_ci			i + 1);
25562306a36Sopenharmony_ci		break;
25662306a36Sopenharmony_ci	case IRQ_TYPE_MSIX:
25762306a36Sopenharmony_ci		dev_err(dev, "Failed to request IRQ %d for MSI-X %d\n",
25862306a36Sopenharmony_ci			pci_irq_vector(pdev, i),
25962306a36Sopenharmony_ci			i + 1);
26062306a36Sopenharmony_ci		break;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	return false;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic bool pci_endpoint_test_bar(struct pci_endpoint_test *test,
26762306a36Sopenharmony_ci				  enum pci_barno barno)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	int j;
27062306a36Sopenharmony_ci	u32 val;
27162306a36Sopenharmony_ci	int size;
27262306a36Sopenharmony_ci	struct pci_dev *pdev = test->pdev;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (!test->bar[barno])
27562306a36Sopenharmony_ci		return false;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	size = pci_resource_len(pdev, barno);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (barno == test->test_reg_bar)
28062306a36Sopenharmony_ci		size = 0x4;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	for (j = 0; j < size; j += 4)
28362306a36Sopenharmony_ci		pci_endpoint_test_bar_writel(test, barno, j, 0xA0A0A0A0);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	for (j = 0; j < size; j += 4) {
28662306a36Sopenharmony_ci		val = pci_endpoint_test_bar_readl(test, barno, j);
28762306a36Sopenharmony_ci		if (val != 0xA0A0A0A0)
28862306a36Sopenharmony_ci			return false;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	return true;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	u32 val;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
29962306a36Sopenharmony_ci				 IRQ_TYPE_LEGACY);
30062306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 0);
30162306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
30262306a36Sopenharmony_ci				 COMMAND_RAISE_LEGACY_IRQ);
30362306a36Sopenharmony_ci	val = wait_for_completion_timeout(&test->irq_raised,
30462306a36Sopenharmony_ci					  msecs_to_jiffies(1000));
30562306a36Sopenharmony_ci	if (!val)
30662306a36Sopenharmony_ci		return false;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return true;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
31262306a36Sopenharmony_ci				       u16 msi_num, bool msix)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	u32 val;
31562306a36Sopenharmony_ci	struct pci_dev *pdev = test->pdev;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
31862306a36Sopenharmony_ci				 msix ? IRQ_TYPE_MSIX : IRQ_TYPE_MSI);
31962306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, msi_num);
32062306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
32162306a36Sopenharmony_ci				 msix ? COMMAND_RAISE_MSIX_IRQ :
32262306a36Sopenharmony_ci				 COMMAND_RAISE_MSI_IRQ);
32362306a36Sopenharmony_ci	val = wait_for_completion_timeout(&test->irq_raised,
32462306a36Sopenharmony_ci					  msecs_to_jiffies(1000));
32562306a36Sopenharmony_ci	if (!val)
32662306a36Sopenharmony_ci		return false;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return pci_irq_vector(pdev, msi_num - 1) == test->last_irq;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic int pci_endpoint_test_validate_xfer_params(struct device *dev,
33262306a36Sopenharmony_ci		struct pci_endpoint_test_xfer_param *param, size_t alignment)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	if (!param->size) {
33562306a36Sopenharmony_ci		dev_dbg(dev, "Data size is zero\n");
33662306a36Sopenharmony_ci		return -EINVAL;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (param->size > SIZE_MAX - alignment) {
34062306a36Sopenharmony_ci		dev_dbg(dev, "Maximum transfer data size exceeded\n");
34162306a36Sopenharmony_ci		return -EINVAL;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return 0;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic bool pci_endpoint_test_copy(struct pci_endpoint_test *test,
34862306a36Sopenharmony_ci				   unsigned long arg)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct pci_endpoint_test_xfer_param param;
35162306a36Sopenharmony_ci	bool ret = false;
35262306a36Sopenharmony_ci	void *src_addr;
35362306a36Sopenharmony_ci	void *dst_addr;
35462306a36Sopenharmony_ci	u32 flags = 0;
35562306a36Sopenharmony_ci	bool use_dma;
35662306a36Sopenharmony_ci	size_t size;
35762306a36Sopenharmony_ci	dma_addr_t src_phys_addr;
35862306a36Sopenharmony_ci	dma_addr_t dst_phys_addr;
35962306a36Sopenharmony_ci	struct pci_dev *pdev = test->pdev;
36062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
36162306a36Sopenharmony_ci	void *orig_src_addr;
36262306a36Sopenharmony_ci	dma_addr_t orig_src_phys_addr;
36362306a36Sopenharmony_ci	void *orig_dst_addr;
36462306a36Sopenharmony_ci	dma_addr_t orig_dst_phys_addr;
36562306a36Sopenharmony_ci	size_t offset;
36662306a36Sopenharmony_ci	size_t alignment = test->alignment;
36762306a36Sopenharmony_ci	int irq_type = test->irq_type;
36862306a36Sopenharmony_ci	u32 src_crc32;
36962306a36Sopenharmony_ci	u32 dst_crc32;
37062306a36Sopenharmony_ci	int err;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	err = copy_from_user(&param, (void __user *)arg, sizeof(param));
37362306a36Sopenharmony_ci	if (err) {
37462306a36Sopenharmony_ci		dev_err(dev, "Failed to get transfer param\n");
37562306a36Sopenharmony_ci		return false;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	err = pci_endpoint_test_validate_xfer_params(dev, &param, alignment);
37962306a36Sopenharmony_ci	if (err)
38062306a36Sopenharmony_ci		return false;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	size = param.size;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA);
38562306a36Sopenharmony_ci	if (use_dma)
38662306a36Sopenharmony_ci		flags |= FLAG_USE_DMA;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
38962306a36Sopenharmony_ci		dev_err(dev, "Invalid IRQ type option\n");
39062306a36Sopenharmony_ci		goto err;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	orig_src_addr = kzalloc(size + alignment, GFP_KERNEL);
39462306a36Sopenharmony_ci	if (!orig_src_addr) {
39562306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate source buffer\n");
39662306a36Sopenharmony_ci		ret = false;
39762306a36Sopenharmony_ci		goto err;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	get_random_bytes(orig_src_addr, size + alignment);
40162306a36Sopenharmony_ci	orig_src_phys_addr = dma_map_single(dev, orig_src_addr,
40262306a36Sopenharmony_ci					    size + alignment, DMA_TO_DEVICE);
40362306a36Sopenharmony_ci	if (dma_mapping_error(dev, orig_src_phys_addr)) {
40462306a36Sopenharmony_ci		dev_err(dev, "failed to map source buffer address\n");
40562306a36Sopenharmony_ci		ret = false;
40662306a36Sopenharmony_ci		goto err_src_phys_addr;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (alignment && !IS_ALIGNED(orig_src_phys_addr, alignment)) {
41062306a36Sopenharmony_ci		src_phys_addr = PTR_ALIGN(orig_src_phys_addr, alignment);
41162306a36Sopenharmony_ci		offset = src_phys_addr - orig_src_phys_addr;
41262306a36Sopenharmony_ci		src_addr = orig_src_addr + offset;
41362306a36Sopenharmony_ci	} else {
41462306a36Sopenharmony_ci		src_phys_addr = orig_src_phys_addr;
41562306a36Sopenharmony_ci		src_addr = orig_src_addr;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_SRC_ADDR,
41962306a36Sopenharmony_ci				 lower_32_bits(src_phys_addr));
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR,
42262306a36Sopenharmony_ci				 upper_32_bits(src_phys_addr));
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	src_crc32 = crc32_le(~0, src_addr, size);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	orig_dst_addr = kzalloc(size + alignment, GFP_KERNEL);
42762306a36Sopenharmony_ci	if (!orig_dst_addr) {
42862306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate destination address\n");
42962306a36Sopenharmony_ci		ret = false;
43062306a36Sopenharmony_ci		goto err_dst_addr;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	orig_dst_phys_addr = dma_map_single(dev, orig_dst_addr,
43462306a36Sopenharmony_ci					    size + alignment, DMA_FROM_DEVICE);
43562306a36Sopenharmony_ci	if (dma_mapping_error(dev, orig_dst_phys_addr)) {
43662306a36Sopenharmony_ci		dev_err(dev, "failed to map destination buffer address\n");
43762306a36Sopenharmony_ci		ret = false;
43862306a36Sopenharmony_ci		goto err_dst_phys_addr;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (alignment && !IS_ALIGNED(orig_dst_phys_addr, alignment)) {
44262306a36Sopenharmony_ci		dst_phys_addr = PTR_ALIGN(orig_dst_phys_addr, alignment);
44362306a36Sopenharmony_ci		offset = dst_phys_addr - orig_dst_phys_addr;
44462306a36Sopenharmony_ci		dst_addr = orig_dst_addr + offset;
44562306a36Sopenharmony_ci	} else {
44662306a36Sopenharmony_ci		dst_phys_addr = orig_dst_phys_addr;
44762306a36Sopenharmony_ci		dst_addr = orig_dst_addr;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_DST_ADDR,
45162306a36Sopenharmony_ci				 lower_32_bits(dst_phys_addr));
45262306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_DST_ADDR,
45362306a36Sopenharmony_ci				 upper_32_bits(dst_phys_addr));
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE,
45662306a36Sopenharmony_ci				 size);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_FLAGS, flags);
45962306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
46062306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
46162306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
46262306a36Sopenharmony_ci				 COMMAND_COPY);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	wait_for_completion(&test->irq_raised);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	dma_unmap_single(dev, orig_dst_phys_addr, size + alignment,
46762306a36Sopenharmony_ci			 DMA_FROM_DEVICE);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	dst_crc32 = crc32_le(~0, dst_addr, size);
47062306a36Sopenharmony_ci	if (dst_crc32 == src_crc32)
47162306a36Sopenharmony_ci		ret = true;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cierr_dst_phys_addr:
47462306a36Sopenharmony_ci	kfree(orig_dst_addr);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cierr_dst_addr:
47762306a36Sopenharmony_ci	dma_unmap_single(dev, orig_src_phys_addr, size + alignment,
47862306a36Sopenharmony_ci			 DMA_TO_DEVICE);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cierr_src_phys_addr:
48162306a36Sopenharmony_ci	kfree(orig_src_addr);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cierr:
48462306a36Sopenharmony_ci	return ret;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic bool pci_endpoint_test_write(struct pci_endpoint_test *test,
48862306a36Sopenharmony_ci				    unsigned long arg)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct pci_endpoint_test_xfer_param param;
49162306a36Sopenharmony_ci	bool ret = false;
49262306a36Sopenharmony_ci	u32 flags = 0;
49362306a36Sopenharmony_ci	bool use_dma;
49462306a36Sopenharmony_ci	u32 reg;
49562306a36Sopenharmony_ci	void *addr;
49662306a36Sopenharmony_ci	dma_addr_t phys_addr;
49762306a36Sopenharmony_ci	struct pci_dev *pdev = test->pdev;
49862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
49962306a36Sopenharmony_ci	void *orig_addr;
50062306a36Sopenharmony_ci	dma_addr_t orig_phys_addr;
50162306a36Sopenharmony_ci	size_t offset;
50262306a36Sopenharmony_ci	size_t alignment = test->alignment;
50362306a36Sopenharmony_ci	int irq_type = test->irq_type;
50462306a36Sopenharmony_ci	size_t size;
50562306a36Sopenharmony_ci	u32 crc32;
50662306a36Sopenharmony_ci	int err;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	err = copy_from_user(&param, (void __user *)arg, sizeof(param));
50962306a36Sopenharmony_ci	if (err != 0) {
51062306a36Sopenharmony_ci		dev_err(dev, "Failed to get transfer param\n");
51162306a36Sopenharmony_ci		return false;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	err = pci_endpoint_test_validate_xfer_params(dev, &param, alignment);
51562306a36Sopenharmony_ci	if (err)
51662306a36Sopenharmony_ci		return false;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	size = param.size;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA);
52162306a36Sopenharmony_ci	if (use_dma)
52262306a36Sopenharmony_ci		flags |= FLAG_USE_DMA;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
52562306a36Sopenharmony_ci		dev_err(dev, "Invalid IRQ type option\n");
52662306a36Sopenharmony_ci		goto err;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	orig_addr = kzalloc(size + alignment, GFP_KERNEL);
53062306a36Sopenharmony_ci	if (!orig_addr) {
53162306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate address\n");
53262306a36Sopenharmony_ci		ret = false;
53362306a36Sopenharmony_ci		goto err;
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	get_random_bytes(orig_addr, size + alignment);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	orig_phys_addr = dma_map_single(dev, orig_addr, size + alignment,
53962306a36Sopenharmony_ci					DMA_TO_DEVICE);
54062306a36Sopenharmony_ci	if (dma_mapping_error(dev, orig_phys_addr)) {
54162306a36Sopenharmony_ci		dev_err(dev, "failed to map source buffer address\n");
54262306a36Sopenharmony_ci		ret = false;
54362306a36Sopenharmony_ci		goto err_phys_addr;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (alignment && !IS_ALIGNED(orig_phys_addr, alignment)) {
54762306a36Sopenharmony_ci		phys_addr =  PTR_ALIGN(orig_phys_addr, alignment);
54862306a36Sopenharmony_ci		offset = phys_addr - orig_phys_addr;
54962306a36Sopenharmony_ci		addr = orig_addr + offset;
55062306a36Sopenharmony_ci	} else {
55162306a36Sopenharmony_ci		phys_addr = orig_phys_addr;
55262306a36Sopenharmony_ci		addr = orig_addr;
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	crc32 = crc32_le(~0, addr, size);
55662306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_CHECKSUM,
55762306a36Sopenharmony_ci				 crc32);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_SRC_ADDR,
56062306a36Sopenharmony_ci				 lower_32_bits(phys_addr));
56162306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR,
56262306a36Sopenharmony_ci				 upper_32_bits(phys_addr));
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_FLAGS, flags);
56762306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
56862306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
56962306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
57062306a36Sopenharmony_ci				 COMMAND_READ);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	wait_for_completion(&test->irq_raised);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
57562306a36Sopenharmony_ci	if (reg & STATUS_READ_SUCCESS)
57662306a36Sopenharmony_ci		ret = true;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	dma_unmap_single(dev, orig_phys_addr, size + alignment,
57962306a36Sopenharmony_ci			 DMA_TO_DEVICE);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cierr_phys_addr:
58262306a36Sopenharmony_ci	kfree(orig_addr);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cierr:
58562306a36Sopenharmony_ci	return ret;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic bool pci_endpoint_test_read(struct pci_endpoint_test *test,
58962306a36Sopenharmony_ci				   unsigned long arg)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct pci_endpoint_test_xfer_param param;
59262306a36Sopenharmony_ci	bool ret = false;
59362306a36Sopenharmony_ci	u32 flags = 0;
59462306a36Sopenharmony_ci	bool use_dma;
59562306a36Sopenharmony_ci	size_t size;
59662306a36Sopenharmony_ci	void *addr;
59762306a36Sopenharmony_ci	dma_addr_t phys_addr;
59862306a36Sopenharmony_ci	struct pci_dev *pdev = test->pdev;
59962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
60062306a36Sopenharmony_ci	void *orig_addr;
60162306a36Sopenharmony_ci	dma_addr_t orig_phys_addr;
60262306a36Sopenharmony_ci	size_t offset;
60362306a36Sopenharmony_ci	size_t alignment = test->alignment;
60462306a36Sopenharmony_ci	int irq_type = test->irq_type;
60562306a36Sopenharmony_ci	u32 crc32;
60662306a36Sopenharmony_ci	int err;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	err = copy_from_user(&param, (void __user *)arg, sizeof(param));
60962306a36Sopenharmony_ci	if (err) {
61062306a36Sopenharmony_ci		dev_err(dev, "Failed to get transfer param\n");
61162306a36Sopenharmony_ci		return false;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	err = pci_endpoint_test_validate_xfer_params(dev, &param, alignment);
61562306a36Sopenharmony_ci	if (err)
61662306a36Sopenharmony_ci		return false;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	size = param.size;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA);
62162306a36Sopenharmony_ci	if (use_dma)
62262306a36Sopenharmony_ci		flags |= FLAG_USE_DMA;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
62562306a36Sopenharmony_ci		dev_err(dev, "Invalid IRQ type option\n");
62662306a36Sopenharmony_ci		goto err;
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	orig_addr = kzalloc(size + alignment, GFP_KERNEL);
63062306a36Sopenharmony_ci	if (!orig_addr) {
63162306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate destination address\n");
63262306a36Sopenharmony_ci		ret = false;
63362306a36Sopenharmony_ci		goto err;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	orig_phys_addr = dma_map_single(dev, orig_addr, size + alignment,
63762306a36Sopenharmony_ci					DMA_FROM_DEVICE);
63862306a36Sopenharmony_ci	if (dma_mapping_error(dev, orig_phys_addr)) {
63962306a36Sopenharmony_ci		dev_err(dev, "failed to map source buffer address\n");
64062306a36Sopenharmony_ci		ret = false;
64162306a36Sopenharmony_ci		goto err_phys_addr;
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (alignment && !IS_ALIGNED(orig_phys_addr, alignment)) {
64562306a36Sopenharmony_ci		phys_addr = PTR_ALIGN(orig_phys_addr, alignment);
64662306a36Sopenharmony_ci		offset = phys_addr - orig_phys_addr;
64762306a36Sopenharmony_ci		addr = orig_addr + offset;
64862306a36Sopenharmony_ci	} else {
64962306a36Sopenharmony_ci		phys_addr = orig_phys_addr;
65062306a36Sopenharmony_ci		addr = orig_addr;
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_DST_ADDR,
65462306a36Sopenharmony_ci				 lower_32_bits(phys_addr));
65562306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_DST_ADDR,
65662306a36Sopenharmony_ci				 upper_32_bits(phys_addr));
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_FLAGS, flags);
66162306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
66262306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
66362306a36Sopenharmony_ci	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
66462306a36Sopenharmony_ci				 COMMAND_WRITE);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	wait_for_completion(&test->irq_raised);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	dma_unmap_single(dev, orig_phys_addr, size + alignment,
66962306a36Sopenharmony_ci			 DMA_FROM_DEVICE);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	crc32 = crc32_le(~0, addr, size);
67262306a36Sopenharmony_ci	if (crc32 == pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM))
67362306a36Sopenharmony_ci		ret = true;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cierr_phys_addr:
67662306a36Sopenharmony_ci	kfree(orig_addr);
67762306a36Sopenharmony_cierr:
67862306a36Sopenharmony_ci	return ret;
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_cistatic bool pci_endpoint_test_clear_irq(struct pci_endpoint_test *test)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	pci_endpoint_test_release_irq(test);
68462306a36Sopenharmony_ci	pci_endpoint_test_free_irq_vectors(test);
68562306a36Sopenharmony_ci	return true;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic bool pci_endpoint_test_set_irq(struct pci_endpoint_test *test,
68962306a36Sopenharmony_ci				      int req_irq_type)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	struct pci_dev *pdev = test->pdev;
69262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (req_irq_type < IRQ_TYPE_LEGACY || req_irq_type > IRQ_TYPE_MSIX) {
69562306a36Sopenharmony_ci		dev_err(dev, "Invalid IRQ type option\n");
69662306a36Sopenharmony_ci		return false;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	if (test->irq_type == req_irq_type)
70062306a36Sopenharmony_ci		return true;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	pci_endpoint_test_release_irq(test);
70362306a36Sopenharmony_ci	pci_endpoint_test_free_irq_vectors(test);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (!pci_endpoint_test_alloc_irq_vectors(test, req_irq_type))
70662306a36Sopenharmony_ci		goto err;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (!pci_endpoint_test_request_irq(test))
70962306a36Sopenharmony_ci		goto err;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	return true;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cierr:
71462306a36Sopenharmony_ci	pci_endpoint_test_free_irq_vectors(test);
71562306a36Sopenharmony_ci	return false;
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_cistatic long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
71962306a36Sopenharmony_ci				    unsigned long arg)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	int ret = -EINVAL;
72262306a36Sopenharmony_ci	enum pci_barno bar;
72362306a36Sopenharmony_ci	struct pci_endpoint_test *test = to_endpoint_test(file->private_data);
72462306a36Sopenharmony_ci	struct pci_dev *pdev = test->pdev;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	mutex_lock(&test->mutex);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	reinit_completion(&test->irq_raised);
72962306a36Sopenharmony_ci	test->last_irq = -ENODATA;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	switch (cmd) {
73262306a36Sopenharmony_ci	case PCITEST_BAR:
73362306a36Sopenharmony_ci		bar = arg;
73462306a36Sopenharmony_ci		if (bar > BAR_5)
73562306a36Sopenharmony_ci			goto ret;
73662306a36Sopenharmony_ci		if (is_am654_pci_dev(pdev) && bar == BAR_0)
73762306a36Sopenharmony_ci			goto ret;
73862306a36Sopenharmony_ci		ret = pci_endpoint_test_bar(test, bar);
73962306a36Sopenharmony_ci		break;
74062306a36Sopenharmony_ci	case PCITEST_LEGACY_IRQ:
74162306a36Sopenharmony_ci		ret = pci_endpoint_test_legacy_irq(test);
74262306a36Sopenharmony_ci		break;
74362306a36Sopenharmony_ci	case PCITEST_MSI:
74462306a36Sopenharmony_ci	case PCITEST_MSIX:
74562306a36Sopenharmony_ci		ret = pci_endpoint_test_msi_irq(test, arg, cmd == PCITEST_MSIX);
74662306a36Sopenharmony_ci		break;
74762306a36Sopenharmony_ci	case PCITEST_WRITE:
74862306a36Sopenharmony_ci		ret = pci_endpoint_test_write(test, arg);
74962306a36Sopenharmony_ci		break;
75062306a36Sopenharmony_ci	case PCITEST_READ:
75162306a36Sopenharmony_ci		ret = pci_endpoint_test_read(test, arg);
75262306a36Sopenharmony_ci		break;
75362306a36Sopenharmony_ci	case PCITEST_COPY:
75462306a36Sopenharmony_ci		ret = pci_endpoint_test_copy(test, arg);
75562306a36Sopenharmony_ci		break;
75662306a36Sopenharmony_ci	case PCITEST_SET_IRQTYPE:
75762306a36Sopenharmony_ci		ret = pci_endpoint_test_set_irq(test, arg);
75862306a36Sopenharmony_ci		break;
75962306a36Sopenharmony_ci	case PCITEST_GET_IRQTYPE:
76062306a36Sopenharmony_ci		ret = irq_type;
76162306a36Sopenharmony_ci		break;
76262306a36Sopenharmony_ci	case PCITEST_CLEAR_IRQ:
76362306a36Sopenharmony_ci		ret = pci_endpoint_test_clear_irq(test);
76462306a36Sopenharmony_ci		break;
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ciret:
76862306a36Sopenharmony_ci	mutex_unlock(&test->mutex);
76962306a36Sopenharmony_ci	return ret;
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistatic const struct file_operations pci_endpoint_test_fops = {
77362306a36Sopenharmony_ci	.owner = THIS_MODULE,
77462306a36Sopenharmony_ci	.unlocked_ioctl = pci_endpoint_test_ioctl,
77562306a36Sopenharmony_ci};
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cistatic int pci_endpoint_test_probe(struct pci_dev *pdev,
77862306a36Sopenharmony_ci				   const struct pci_device_id *ent)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	int err;
78162306a36Sopenharmony_ci	int id;
78262306a36Sopenharmony_ci	char name[24];
78362306a36Sopenharmony_ci	enum pci_barno bar;
78462306a36Sopenharmony_ci	void __iomem *base;
78562306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
78662306a36Sopenharmony_ci	struct pci_endpoint_test *test;
78762306a36Sopenharmony_ci	struct pci_endpoint_test_data *data;
78862306a36Sopenharmony_ci	enum pci_barno test_reg_bar = BAR_0;
78962306a36Sopenharmony_ci	struct miscdevice *misc_device;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (pci_is_bridge(pdev))
79262306a36Sopenharmony_ci		return -ENODEV;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	test = devm_kzalloc(dev, sizeof(*test), GFP_KERNEL);
79562306a36Sopenharmony_ci	if (!test)
79662306a36Sopenharmony_ci		return -ENOMEM;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	test->test_reg_bar = 0;
79962306a36Sopenharmony_ci	test->alignment = 0;
80062306a36Sopenharmony_ci	test->pdev = pdev;
80162306a36Sopenharmony_ci	test->irq_type = IRQ_TYPE_UNDEFINED;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	if (no_msi)
80462306a36Sopenharmony_ci		irq_type = IRQ_TYPE_LEGACY;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	data = (struct pci_endpoint_test_data *)ent->driver_data;
80762306a36Sopenharmony_ci	if (data) {
80862306a36Sopenharmony_ci		test_reg_bar = data->test_reg_bar;
80962306a36Sopenharmony_ci		test->test_reg_bar = test_reg_bar;
81062306a36Sopenharmony_ci		test->alignment = data->alignment;
81162306a36Sopenharmony_ci		irq_type = data->irq_type;
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	init_completion(&test->irq_raised);
81562306a36Sopenharmony_ci	mutex_init(&test->mutex);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if ((dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48)) != 0) &&
81862306a36Sopenharmony_ci	    dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)) != 0) {
81962306a36Sopenharmony_ci		dev_err(dev, "Cannot set DMA mask\n");
82062306a36Sopenharmony_ci		return -EINVAL;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	err = pci_enable_device(pdev);
82462306a36Sopenharmony_ci	if (err) {
82562306a36Sopenharmony_ci		dev_err(dev, "Cannot enable PCI device\n");
82662306a36Sopenharmony_ci		return err;
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	err = pci_request_regions(pdev, DRV_MODULE_NAME);
83062306a36Sopenharmony_ci	if (err) {
83162306a36Sopenharmony_ci		dev_err(dev, "Cannot obtain PCI resources\n");
83262306a36Sopenharmony_ci		goto err_disable_pdev;
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	pci_set_master(pdev);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	if (!pci_endpoint_test_alloc_irq_vectors(test, irq_type)) {
83862306a36Sopenharmony_ci		err = -EINVAL;
83962306a36Sopenharmony_ci		goto err_disable_irq;
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
84362306a36Sopenharmony_ci		if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
84462306a36Sopenharmony_ci			base = pci_ioremap_bar(pdev, bar);
84562306a36Sopenharmony_ci			if (!base) {
84662306a36Sopenharmony_ci				dev_err(dev, "Failed to read BAR%d\n", bar);
84762306a36Sopenharmony_ci				WARN_ON(bar == test_reg_bar);
84862306a36Sopenharmony_ci			}
84962306a36Sopenharmony_ci			test->bar[bar] = base;
85062306a36Sopenharmony_ci		}
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	test->base = test->bar[test_reg_bar];
85462306a36Sopenharmony_ci	if (!test->base) {
85562306a36Sopenharmony_ci		err = -ENOMEM;
85662306a36Sopenharmony_ci		dev_err(dev, "Cannot perform PCI test without BAR%d\n",
85762306a36Sopenharmony_ci			test_reg_bar);
85862306a36Sopenharmony_ci		goto err_iounmap;
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	pci_set_drvdata(pdev, test);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	id = ida_simple_get(&pci_endpoint_test_ida, 0, 0, GFP_KERNEL);
86462306a36Sopenharmony_ci	if (id < 0) {
86562306a36Sopenharmony_ci		err = id;
86662306a36Sopenharmony_ci		dev_err(dev, "Unable to get id\n");
86762306a36Sopenharmony_ci		goto err_iounmap;
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id);
87162306a36Sopenharmony_ci	test->name = kstrdup(name, GFP_KERNEL);
87262306a36Sopenharmony_ci	if (!test->name) {
87362306a36Sopenharmony_ci		err = -ENOMEM;
87462306a36Sopenharmony_ci		goto err_ida_remove;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (!pci_endpoint_test_request_irq(test)) {
87862306a36Sopenharmony_ci		err = -EINVAL;
87962306a36Sopenharmony_ci		goto err_kfree_test_name;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	misc_device = &test->miscdev;
88362306a36Sopenharmony_ci	misc_device->minor = MISC_DYNAMIC_MINOR;
88462306a36Sopenharmony_ci	misc_device->name = kstrdup(name, GFP_KERNEL);
88562306a36Sopenharmony_ci	if (!misc_device->name) {
88662306a36Sopenharmony_ci		err = -ENOMEM;
88762306a36Sopenharmony_ci		goto err_release_irq;
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci	misc_device->parent = &pdev->dev;
89062306a36Sopenharmony_ci	misc_device->fops = &pci_endpoint_test_fops;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	err = misc_register(misc_device);
89362306a36Sopenharmony_ci	if (err) {
89462306a36Sopenharmony_ci		dev_err(dev, "Failed to register device\n");
89562306a36Sopenharmony_ci		goto err_kfree_name;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	return 0;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cierr_kfree_name:
90162306a36Sopenharmony_ci	kfree(misc_device->name);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cierr_release_irq:
90462306a36Sopenharmony_ci	pci_endpoint_test_release_irq(test);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_cierr_kfree_test_name:
90762306a36Sopenharmony_ci	kfree(test->name);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cierr_ida_remove:
91062306a36Sopenharmony_ci	ida_simple_remove(&pci_endpoint_test_ida, id);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cierr_iounmap:
91362306a36Sopenharmony_ci	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
91462306a36Sopenharmony_ci		if (test->bar[bar])
91562306a36Sopenharmony_ci			pci_iounmap(pdev, test->bar[bar]);
91662306a36Sopenharmony_ci	}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cierr_disable_irq:
91962306a36Sopenharmony_ci	pci_endpoint_test_free_irq_vectors(test);
92062306a36Sopenharmony_ci	pci_release_regions(pdev);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cierr_disable_pdev:
92362306a36Sopenharmony_ci	pci_disable_device(pdev);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	return err;
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_cistatic void pci_endpoint_test_remove(struct pci_dev *pdev)
92962306a36Sopenharmony_ci{
93062306a36Sopenharmony_ci	int id;
93162306a36Sopenharmony_ci	enum pci_barno bar;
93262306a36Sopenharmony_ci	struct pci_endpoint_test *test = pci_get_drvdata(pdev);
93362306a36Sopenharmony_ci	struct miscdevice *misc_device = &test->miscdev;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	if (sscanf(misc_device->name, DRV_MODULE_NAME ".%d", &id) != 1)
93662306a36Sopenharmony_ci		return;
93762306a36Sopenharmony_ci	if (id < 0)
93862306a36Sopenharmony_ci		return;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	pci_endpoint_test_release_irq(test);
94162306a36Sopenharmony_ci	pci_endpoint_test_free_irq_vectors(test);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	misc_deregister(&test->miscdev);
94462306a36Sopenharmony_ci	kfree(misc_device->name);
94562306a36Sopenharmony_ci	kfree(test->name);
94662306a36Sopenharmony_ci	ida_simple_remove(&pci_endpoint_test_ida, id);
94762306a36Sopenharmony_ci	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
94862306a36Sopenharmony_ci		if (test->bar[bar])
94962306a36Sopenharmony_ci			pci_iounmap(pdev, test->bar[bar]);
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	pci_release_regions(pdev);
95362306a36Sopenharmony_ci	pci_disable_device(pdev);
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic const struct pci_endpoint_test_data default_data = {
95762306a36Sopenharmony_ci	.test_reg_bar = BAR_0,
95862306a36Sopenharmony_ci	.alignment = SZ_4K,
95962306a36Sopenharmony_ci	.irq_type = IRQ_TYPE_MSI,
96062306a36Sopenharmony_ci};
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic const struct pci_endpoint_test_data am654_data = {
96362306a36Sopenharmony_ci	.test_reg_bar = BAR_2,
96462306a36Sopenharmony_ci	.alignment = SZ_64K,
96562306a36Sopenharmony_ci	.irq_type = IRQ_TYPE_MSI,
96662306a36Sopenharmony_ci};
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic const struct pci_endpoint_test_data j721e_data = {
96962306a36Sopenharmony_ci	.alignment = 256,
97062306a36Sopenharmony_ci	.irq_type = IRQ_TYPE_MSI,
97162306a36Sopenharmony_ci};
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic const struct pci_device_id pci_endpoint_test_tbl[] = {
97462306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x),
97562306a36Sopenharmony_ci	  .driver_data = (kernel_ulong_t)&default_data,
97662306a36Sopenharmony_ci	},
97762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x),
97862306a36Sopenharmony_ci	  .driver_data = (kernel_ulong_t)&default_data,
97962306a36Sopenharmony_ci	},
98062306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0),
98162306a36Sopenharmony_ci	  .driver_data = (kernel_ulong_t)&default_data,
98262306a36Sopenharmony_ci	},
98362306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8),},
98462306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_LS1088A),
98562306a36Sopenharmony_ci	  .driver_data = (kernel_ulong_t)&default_data,
98662306a36Sopenharmony_ci	},
98762306a36Sopenharmony_ci	{ PCI_DEVICE_DATA(SYNOPSYS, EDDA, NULL) },
98862306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM654),
98962306a36Sopenharmony_ci	  .driver_data = (kernel_ulong_t)&am654_data
99062306a36Sopenharmony_ci	},
99162306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774A1),},
99262306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1),},
99362306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0),},
99462306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1),},
99562306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A779F0),
99662306a36Sopenharmony_ci	  .driver_data = (kernel_ulong_t)&default_data,
99762306a36Sopenharmony_ci	},
99862306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721E),
99962306a36Sopenharmony_ci	  .driver_data = (kernel_ulong_t)&j721e_data,
100062306a36Sopenharmony_ci	},
100162306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J7200),
100262306a36Sopenharmony_ci	  .driver_data = (kernel_ulong_t)&j721e_data,
100362306a36Sopenharmony_ci	},
100462306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM64),
100562306a36Sopenharmony_ci	  .driver_data = (kernel_ulong_t)&j721e_data,
100662306a36Sopenharmony_ci	},
100762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721S2),
100862306a36Sopenharmony_ci	  .driver_data = (kernel_ulong_t)&j721e_data,
100962306a36Sopenharmony_ci	},
101062306a36Sopenharmony_ci	{ }
101162306a36Sopenharmony_ci};
101262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_cistatic struct pci_driver pci_endpoint_test_driver = {
101562306a36Sopenharmony_ci	.name		= DRV_MODULE_NAME,
101662306a36Sopenharmony_ci	.id_table	= pci_endpoint_test_tbl,
101762306a36Sopenharmony_ci	.probe		= pci_endpoint_test_probe,
101862306a36Sopenharmony_ci	.remove		= pci_endpoint_test_remove,
101962306a36Sopenharmony_ci	.sriov_configure = pci_sriov_configure_simple,
102062306a36Sopenharmony_ci};
102162306a36Sopenharmony_cimodule_pci_driver(pci_endpoint_test_driver);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ciMODULE_DESCRIPTION("PCI ENDPOINT TEST HOST DRIVER");
102462306a36Sopenharmony_ciMODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
102562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1026