xref: /openbmc/linux/drivers/misc/pci_endpoint_test.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
16b1baefeSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21aa3f2b0SRandy Dunlap /*
32c156ac7SKishon Vijay Abraham I  * Host side test driver to test endpoint functionality
42c156ac7SKishon Vijay Abraham I  *
52c156ac7SKishon Vijay Abraham I  * Copyright (C) 2017 Texas Instruments
62c156ac7SKishon Vijay Abraham I  * Author: Kishon Vijay Abraham I <kishon@ti.com>
72c156ac7SKishon Vijay Abraham I  */
82c156ac7SKishon Vijay Abraham I 
92c156ac7SKishon Vijay Abraham I #include <linux/crc32.h>
102c156ac7SKishon Vijay Abraham I #include <linux/delay.h>
112c156ac7SKishon Vijay Abraham I #include <linux/fs.h>
122c156ac7SKishon Vijay Abraham I #include <linux/io.h>
132c156ac7SKishon Vijay Abraham I #include <linux/interrupt.h>
142c156ac7SKishon Vijay Abraham I #include <linux/irq.h>
152c156ac7SKishon Vijay Abraham I #include <linux/miscdevice.h>
162c156ac7SKishon Vijay Abraham I #include <linux/module.h>
172c156ac7SKishon Vijay Abraham I #include <linux/mutex.h>
182c156ac7SKishon Vijay Abraham I #include <linux/random.h>
192c156ac7SKishon Vijay Abraham I #include <linux/slab.h>
20cf376b4bSKishon Vijay Abraham I #include <linux/uaccess.h>
212c156ac7SKishon Vijay Abraham I #include <linux/pci.h>
222c156ac7SKishon Vijay Abraham I #include <linux/pci_ids.h>
232c156ac7SKishon Vijay Abraham I 
242c156ac7SKishon Vijay Abraham I #include <linux/pci_regs.h>
252c156ac7SKishon Vijay Abraham I 
262c156ac7SKishon Vijay Abraham I #include <uapi/linux/pcitest.h>
272c156ac7SKishon Vijay Abraham I 
282c156ac7SKishon Vijay Abraham I #define DRV_MODULE_NAME				"pci-endpoint-test"
292c156ac7SKishon Vijay Abraham I 
30e0332712SGustavo Pimentel #define IRQ_TYPE_UNDEFINED			-1
31e8817de7SGustavo Pimentel #define IRQ_TYPE_LEGACY				0
32e8817de7SGustavo Pimentel #define IRQ_TYPE_MSI				1
33c2e00e31SGustavo Pimentel #define IRQ_TYPE_MSIX				2
34e8817de7SGustavo Pimentel 
352c156ac7SKishon Vijay Abraham I #define PCI_ENDPOINT_TEST_MAGIC			0x0
362c156ac7SKishon Vijay Abraham I 
372c156ac7SKishon Vijay Abraham I #define PCI_ENDPOINT_TEST_COMMAND		0x4
382c156ac7SKishon Vijay Abraham I #define COMMAND_RAISE_LEGACY_IRQ		BIT(0)
392c156ac7SKishon Vijay Abraham I #define COMMAND_RAISE_MSI_IRQ			BIT(1)
40c2e00e31SGustavo Pimentel #define COMMAND_RAISE_MSIX_IRQ			BIT(2)
41e8817de7SGustavo Pimentel #define COMMAND_READ				BIT(3)
42e8817de7SGustavo Pimentel #define COMMAND_WRITE				BIT(4)
43e8817de7SGustavo Pimentel #define COMMAND_COPY				BIT(5)
442c156ac7SKishon Vijay Abraham I 
452c156ac7SKishon Vijay Abraham I #define PCI_ENDPOINT_TEST_STATUS		0x8
462c156ac7SKishon Vijay Abraham I #define STATUS_READ_SUCCESS			BIT(0)
472c156ac7SKishon Vijay Abraham I #define STATUS_READ_FAIL			BIT(1)
482c156ac7SKishon Vijay Abraham I #define STATUS_WRITE_SUCCESS			BIT(2)
492c156ac7SKishon Vijay Abraham I #define STATUS_WRITE_FAIL			BIT(3)
502c156ac7SKishon Vijay Abraham I #define STATUS_COPY_SUCCESS			BIT(4)
512c156ac7SKishon Vijay Abraham I #define STATUS_COPY_FAIL			BIT(5)
522c156ac7SKishon Vijay Abraham I #define STATUS_IRQ_RAISED			BIT(6)
532c156ac7SKishon Vijay Abraham I #define STATUS_SRC_ADDR_INVALID			BIT(7)
542c156ac7SKishon Vijay Abraham I #define STATUS_DST_ADDR_INVALID			BIT(8)
552c156ac7SKishon Vijay Abraham I 
56e8817de7SGustavo Pimentel #define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR	0x0c
572c156ac7SKishon Vijay Abraham I #define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR	0x10
582c156ac7SKishon Vijay Abraham I 
592c156ac7SKishon Vijay Abraham I #define PCI_ENDPOINT_TEST_LOWER_DST_ADDR	0x14
602c156ac7SKishon Vijay Abraham I #define PCI_ENDPOINT_TEST_UPPER_DST_ADDR	0x18
612c156ac7SKishon Vijay Abraham I 
622c156ac7SKishon Vijay Abraham I #define PCI_ENDPOINT_TEST_SIZE			0x1c
632c156ac7SKishon Vijay Abraham I #define PCI_ENDPOINT_TEST_CHECKSUM		0x20
642c156ac7SKishon Vijay Abraham I 
65e8817de7SGustavo Pimentel #define PCI_ENDPOINT_TEST_IRQ_TYPE		0x24
66e8817de7SGustavo Pimentel #define PCI_ENDPOINT_TEST_IRQ_NUMBER		0x28
67e8817de7SGustavo Pimentel 
68cf376b4bSKishon Vijay Abraham I #define PCI_ENDPOINT_TEST_FLAGS			0x2c
69cf376b4bSKishon Vijay Abraham I #define FLAG_USE_DMA				BIT(0)
70cf376b4bSKishon Vijay Abraham I 
715bb04b19SKishon Vijay Abraham I #define PCI_DEVICE_ID_TI_AM654			0xb00c
727c52009dSKishon Vijay Abraham I #define PCI_DEVICE_ID_TI_J7200			0xb00f
737c52009dSKishon Vijay Abraham I #define PCI_DEVICE_ID_TI_AM64			0xb010
74be3e3ab5SSiddharth Vadapalli #define PCI_DEVICE_ID_TI_J721S2		0xb013
756b8ab421SXiaowei Bao #define PCI_DEVICE_ID_LS1088A			0x80c0
7601ea5edeSRichard Zhu #define PCI_DEVICE_ID_IMX8			0x0808
775bb04b19SKishon Vijay Abraham I 
785bb04b19SKishon Vijay Abraham I #define is_am654_pci_dev(pdev)		\
795bb04b19SKishon Vijay Abraham I 		((pdev)->device == PCI_DEVICE_ID_TI_AM654)
805bb04b19SKishon Vijay Abraham I 
81cfb824ddSLad Prabhakar #define PCI_DEVICE_ID_RENESAS_R8A774A1		0x0028
82cfb824ddSLad Prabhakar #define PCI_DEVICE_ID_RENESAS_R8A774B1		0x002b
83b03025c5SLad Prabhakar #define PCI_DEVICE_ID_RENESAS_R8A774C0		0x002d
84a63c5f3dSLad Prabhakar #define PCI_DEVICE_ID_RENESAS_R8A774E1		0x0025
85*f22145b5SYoshihiro Shimoda #define PCI_DEVICE_ID_RENESAS_R8A779F0		0x0031
86b03025c5SLad Prabhakar 
872c156ac7SKishon Vijay Abraham I static DEFINE_IDA(pci_endpoint_test_ida);
882c156ac7SKishon Vijay Abraham I 
892c156ac7SKishon Vijay Abraham I #define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \
902c156ac7SKishon Vijay Abraham I 					    miscdev)
910c8a5f9dSKishon Vijay Abraham I 
920c8a5f9dSKishon Vijay Abraham I static bool no_msi;
930c8a5f9dSKishon Vijay Abraham I module_param(no_msi, bool, 0444);
940c8a5f9dSKishon Vijay Abraham I MODULE_PARM_DESC(no_msi, "Disable MSI interrupt in pci_endpoint_test");
950c8a5f9dSKishon Vijay Abraham I 
969133e394SGustavo Pimentel static int irq_type = IRQ_TYPE_MSI;
979133e394SGustavo Pimentel module_param(irq_type, int, 0444);
98c2e00e31SGustavo Pimentel MODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI, 2 - MSI-X)");
999133e394SGustavo Pimentel 
1002c156ac7SKishon Vijay Abraham I enum pci_barno {
1012c156ac7SKishon Vijay Abraham I 	BAR_0,
1022c156ac7SKishon Vijay Abraham I 	BAR_1,
1032c156ac7SKishon Vijay Abraham I 	BAR_2,
1042c156ac7SKishon Vijay Abraham I 	BAR_3,
1052c156ac7SKishon Vijay Abraham I 	BAR_4,
1062c156ac7SKishon Vijay Abraham I 	BAR_5,
1072c156ac7SKishon Vijay Abraham I };
1082c156ac7SKishon Vijay Abraham I 
1092c156ac7SKishon Vijay Abraham I struct pci_endpoint_test {
1102c156ac7SKishon Vijay Abraham I 	struct pci_dev	*pdev;
1112c156ac7SKishon Vijay Abraham I 	void __iomem	*base;
112c9c13ba4SDenis Efremov 	void __iomem	*bar[PCI_STD_NUM_BARS];
1132c156ac7SKishon Vijay Abraham I 	struct completion irq_raised;
1142c156ac7SKishon Vijay Abraham I 	int		last_irq;
115b7636e81SKishon Vijay Abraham I 	int		num_irqs;
116b2ba9225SKishon Vijay Abraham I 	int		irq_type;
1172c156ac7SKishon Vijay Abraham I 	/* mutex to protect the ioctls */
1182c156ac7SKishon Vijay Abraham I 	struct mutex	mutex;
1192c156ac7SKishon Vijay Abraham I 	struct miscdevice miscdev;
120834b9051SKishon Vijay Abraham I 	enum pci_barno test_reg_bar;
12113107c60SKishon Vijay Abraham I 	size_t alignment;
122c2be14abSKishon Vijay Abraham I 	const char *name;
1232c156ac7SKishon Vijay Abraham I };
1242c156ac7SKishon Vijay Abraham I 
125834b9051SKishon Vijay Abraham I struct pci_endpoint_test_data {
126834b9051SKishon Vijay Abraham I 	enum pci_barno test_reg_bar;
12713107c60SKishon Vijay Abraham I 	size_t alignment;
1289133e394SGustavo Pimentel 	int irq_type;
129834b9051SKishon Vijay Abraham I };
130834b9051SKishon Vijay Abraham I 
pci_endpoint_test_readl(struct pci_endpoint_test * test,u32 offset)1312c156ac7SKishon Vijay Abraham I static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test,
1322c156ac7SKishon Vijay Abraham I 					  u32 offset)
1332c156ac7SKishon Vijay Abraham I {
1342c156ac7SKishon Vijay Abraham I 	return readl(test->base + offset);
1352c156ac7SKishon Vijay Abraham I }
1362c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_writel(struct pci_endpoint_test * test,u32 offset,u32 value)1372c156ac7SKishon Vijay Abraham I static inline void pci_endpoint_test_writel(struct pci_endpoint_test *test,
1382c156ac7SKishon Vijay Abraham I 					    u32 offset, u32 value)
1392c156ac7SKishon Vijay Abraham I {
1402c156ac7SKishon Vijay Abraham I 	writel(value, test->base + offset);
1412c156ac7SKishon Vijay Abraham I }
1422c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_bar_readl(struct pci_endpoint_test * test,int bar,int offset)1432c156ac7SKishon Vijay Abraham I static inline u32 pci_endpoint_test_bar_readl(struct pci_endpoint_test *test,
1442c156ac7SKishon Vijay Abraham I 					      int bar, int offset)
1452c156ac7SKishon Vijay Abraham I {
1462c156ac7SKishon Vijay Abraham I 	return readl(test->bar[bar] + offset);
1472c156ac7SKishon Vijay Abraham I }
1482c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_bar_writel(struct pci_endpoint_test * test,int bar,u32 offset,u32 value)1492c156ac7SKishon Vijay Abraham I static inline void pci_endpoint_test_bar_writel(struct pci_endpoint_test *test,
1502c156ac7SKishon Vijay Abraham I 						int bar, u32 offset, u32 value)
1512c156ac7SKishon Vijay Abraham I {
1522c156ac7SKishon Vijay Abraham I 	writel(value, test->bar[bar] + offset);
1532c156ac7SKishon Vijay Abraham I }
1542c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_irqhandler(int irq,void * dev_id)1552c156ac7SKishon Vijay Abraham I static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id)
1562c156ac7SKishon Vijay Abraham I {
1572c156ac7SKishon Vijay Abraham I 	struct pci_endpoint_test *test = dev_id;
1582c156ac7SKishon Vijay Abraham I 	u32 reg;
1592c156ac7SKishon Vijay Abraham I 
1602c156ac7SKishon Vijay Abraham I 	reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
1612c156ac7SKishon Vijay Abraham I 	if (reg & STATUS_IRQ_RAISED) {
1622c156ac7SKishon Vijay Abraham I 		test->last_irq = irq;
1632c156ac7SKishon Vijay Abraham I 		complete(&test->irq_raised);
1642c156ac7SKishon Vijay Abraham I 	}
1652c156ac7SKishon Vijay Abraham I 
1662c156ac7SKishon Vijay Abraham I 	return IRQ_HANDLED;
1672c156ac7SKishon Vijay Abraham I }
1682c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_free_irq_vectors(struct pci_endpoint_test * test)169e0332712SGustavo Pimentel static void pci_endpoint_test_free_irq_vectors(struct pci_endpoint_test *test)
170e0332712SGustavo Pimentel {
171e0332712SGustavo Pimentel 	struct pci_dev *pdev = test->pdev;
172e0332712SGustavo Pimentel 
173e0332712SGustavo Pimentel 	pci_free_irq_vectors(pdev);
174b2ba9225SKishon Vijay Abraham I 	test->irq_type = IRQ_TYPE_UNDEFINED;
175e0332712SGustavo Pimentel }
176e0332712SGustavo Pimentel 
pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test * test,int type)177e0332712SGustavo Pimentel static bool pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test,
178e0332712SGustavo Pimentel 						int type)
179e0332712SGustavo Pimentel {
180e0332712SGustavo Pimentel 	int irq = -1;
181e0332712SGustavo Pimentel 	struct pci_dev *pdev = test->pdev;
182e0332712SGustavo Pimentel 	struct device *dev = &pdev->dev;
183e0332712SGustavo Pimentel 	bool res = true;
184e0332712SGustavo Pimentel 
185e0332712SGustavo Pimentel 	switch (type) {
186e0332712SGustavo Pimentel 	case IRQ_TYPE_LEGACY:
187e0332712SGustavo Pimentel 		irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY);
188e0332712SGustavo Pimentel 		if (irq < 0)
189e0332712SGustavo Pimentel 			dev_err(dev, "Failed to get Legacy interrupt\n");
190e0332712SGustavo Pimentel 		break;
191e0332712SGustavo Pimentel 	case IRQ_TYPE_MSI:
192e0332712SGustavo Pimentel 		irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
193e0332712SGustavo Pimentel 		if (irq < 0)
194e0332712SGustavo Pimentel 			dev_err(dev, "Failed to get MSI interrupts\n");
195e0332712SGustavo Pimentel 		break;
196e0332712SGustavo Pimentel 	case IRQ_TYPE_MSIX:
197e0332712SGustavo Pimentel 		irq = pci_alloc_irq_vectors(pdev, 1, 2048, PCI_IRQ_MSIX);
198e0332712SGustavo Pimentel 		if (irq < 0)
199e0332712SGustavo Pimentel 			dev_err(dev, "Failed to get MSI-X interrupts\n");
200e0332712SGustavo Pimentel 		break;
201e0332712SGustavo Pimentel 	default:
202e0332712SGustavo Pimentel 		dev_err(dev, "Invalid IRQ type selected\n");
203e0332712SGustavo Pimentel 	}
204e0332712SGustavo Pimentel 
205e0332712SGustavo Pimentel 	if (irq < 0) {
206e0332712SGustavo Pimentel 		irq = 0;
207e0332712SGustavo Pimentel 		res = false;
208e0332712SGustavo Pimentel 	}
209b2ba9225SKishon Vijay Abraham I 
210b2ba9225SKishon Vijay Abraham I 	test->irq_type = type;
211e0332712SGustavo Pimentel 	test->num_irqs = irq;
212e0332712SGustavo Pimentel 
213e0332712SGustavo Pimentel 	return res;
214e0332712SGustavo Pimentel }
215e0332712SGustavo Pimentel 
pci_endpoint_test_release_irq(struct pci_endpoint_test * test)216e0332712SGustavo Pimentel static void pci_endpoint_test_release_irq(struct pci_endpoint_test *test)
217e0332712SGustavo Pimentel {
218e0332712SGustavo Pimentel 	int i;
219e0332712SGustavo Pimentel 	struct pci_dev *pdev = test->pdev;
220e0332712SGustavo Pimentel 	struct device *dev = &pdev->dev;
221e0332712SGustavo Pimentel 
222e0332712SGustavo Pimentel 	for (i = 0; i < test->num_irqs; i++)
223e0332712SGustavo Pimentel 		devm_free_irq(dev, pci_irq_vector(pdev, i), test);
224e0332712SGustavo Pimentel 
225e0332712SGustavo Pimentel 	test->num_irqs = 0;
226e0332712SGustavo Pimentel }
227e0332712SGustavo Pimentel 
pci_endpoint_test_request_irq(struct pci_endpoint_test * test)228e0332712SGustavo Pimentel static bool pci_endpoint_test_request_irq(struct pci_endpoint_test *test)
229e0332712SGustavo Pimentel {
230e0332712SGustavo Pimentel 	int i;
231e0332712SGustavo Pimentel 	int err;
232e0332712SGustavo Pimentel 	struct pci_dev *pdev = test->pdev;
233e0332712SGustavo Pimentel 	struct device *dev = &pdev->dev;
234e0332712SGustavo Pimentel 
235e0332712SGustavo Pimentel 	for (i = 0; i < test->num_irqs; i++) {
236e0332712SGustavo Pimentel 		err = devm_request_irq(dev, pci_irq_vector(pdev, i),
237e0332712SGustavo Pimentel 				       pci_endpoint_test_irqhandler,
238c2be14abSKishon Vijay Abraham I 				       IRQF_SHARED, test->name, test);
239e0332712SGustavo Pimentel 		if (err)
240e0332712SGustavo Pimentel 			goto fail;
241e0332712SGustavo Pimentel 	}
242e0332712SGustavo Pimentel 
243e0332712SGustavo Pimentel 	return true;
244e0332712SGustavo Pimentel 
245e0332712SGustavo Pimentel fail:
246e0332712SGustavo Pimentel 	switch (irq_type) {
247e0332712SGustavo Pimentel 	case IRQ_TYPE_LEGACY:
248e0332712SGustavo Pimentel 		dev_err(dev, "Failed to request IRQ %d for Legacy\n",
249e0332712SGustavo Pimentel 			pci_irq_vector(pdev, i));
250e0332712SGustavo Pimentel 		break;
251e0332712SGustavo Pimentel 	case IRQ_TYPE_MSI:
252e0332712SGustavo Pimentel 		dev_err(dev, "Failed to request IRQ %d for MSI %d\n",
253e0332712SGustavo Pimentel 			pci_irq_vector(pdev, i),
254e0332712SGustavo Pimentel 			i + 1);
255e0332712SGustavo Pimentel 		break;
256e0332712SGustavo Pimentel 	case IRQ_TYPE_MSIX:
257e0332712SGustavo Pimentel 		dev_err(dev, "Failed to request IRQ %d for MSI-X %d\n",
258e0332712SGustavo Pimentel 			pci_irq_vector(pdev, i),
259e0332712SGustavo Pimentel 			i + 1);
260e0332712SGustavo Pimentel 		break;
261e0332712SGustavo Pimentel 	}
262e0332712SGustavo Pimentel 
263e0332712SGustavo Pimentel 	return false;
264e0332712SGustavo Pimentel }
265e0332712SGustavo Pimentel 
pci_endpoint_test_bar(struct pci_endpoint_test * test,enum pci_barno barno)2662c156ac7SKishon Vijay Abraham I static bool pci_endpoint_test_bar(struct pci_endpoint_test *test,
2672c156ac7SKishon Vijay Abraham I 				  enum pci_barno barno)
2682c156ac7SKishon Vijay Abraham I {
2692c156ac7SKishon Vijay Abraham I 	int j;
2702c156ac7SKishon Vijay Abraham I 	u32 val;
2712c156ac7SKishon Vijay Abraham I 	int size;
272cda370ecSKishon Vijay Abraham I 	struct pci_dev *pdev = test->pdev;
2732c156ac7SKishon Vijay Abraham I 
2742c156ac7SKishon Vijay Abraham I 	if (!test->bar[barno])
2752c156ac7SKishon Vijay Abraham I 		return false;
2762c156ac7SKishon Vijay Abraham I 
277cda370ecSKishon Vijay Abraham I 	size = pci_resource_len(pdev, barno);
2782c156ac7SKishon Vijay Abraham I 
279834b9051SKishon Vijay Abraham I 	if (barno == test->test_reg_bar)
280834b9051SKishon Vijay Abraham I 		size = 0x4;
281834b9051SKishon Vijay Abraham I 
2822c156ac7SKishon Vijay Abraham I 	for (j = 0; j < size; j += 4)
2832c156ac7SKishon Vijay Abraham I 		pci_endpoint_test_bar_writel(test, barno, j, 0xA0A0A0A0);
2842c156ac7SKishon Vijay Abraham I 
2852c156ac7SKishon Vijay Abraham I 	for (j = 0; j < size; j += 4) {
2862c156ac7SKishon Vijay Abraham I 		val = pci_endpoint_test_bar_readl(test, barno, j);
2872c156ac7SKishon Vijay Abraham I 		if (val != 0xA0A0A0A0)
2882c156ac7SKishon Vijay Abraham I 			return false;
2892c156ac7SKishon Vijay Abraham I 	}
2902c156ac7SKishon Vijay Abraham I 
2912c156ac7SKishon Vijay Abraham I 	return true;
2922c156ac7SKishon Vijay Abraham I }
2932c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_legacy_irq(struct pci_endpoint_test * test)2942c156ac7SKishon Vijay Abraham I static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test)
2952c156ac7SKishon Vijay Abraham I {
2962c156ac7SKishon Vijay Abraham I 	u32 val;
2972c156ac7SKishon Vijay Abraham I 
298e8817de7SGustavo Pimentel 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
299e8817de7SGustavo Pimentel 				 IRQ_TYPE_LEGACY);
300e8817de7SGustavo Pimentel 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 0);
3012c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
3022c156ac7SKishon Vijay Abraham I 				 COMMAND_RAISE_LEGACY_IRQ);
3032c156ac7SKishon Vijay Abraham I 	val = wait_for_completion_timeout(&test->irq_raised,
3042c156ac7SKishon Vijay Abraham I 					  msecs_to_jiffies(1000));
3052c156ac7SKishon Vijay Abraham I 	if (!val)
3062c156ac7SKishon Vijay Abraham I 		return false;
3072c156ac7SKishon Vijay Abraham I 
3082c156ac7SKishon Vijay Abraham I 	return true;
3092c156ac7SKishon Vijay Abraham I }
3102c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_msi_irq(struct pci_endpoint_test * test,u16 msi_num,bool msix)3112c156ac7SKishon Vijay Abraham I static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
312c2e00e31SGustavo Pimentel 				       u16 msi_num, bool msix)
3132c156ac7SKishon Vijay Abraham I {
3142c156ac7SKishon Vijay Abraham I 	u32 val;
3152c156ac7SKishon Vijay Abraham I 	struct pci_dev *pdev = test->pdev;
3162c156ac7SKishon Vijay Abraham I 
317e8817de7SGustavo Pimentel 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
3184c50f933SDamien Le Moal 				 msix ? IRQ_TYPE_MSIX : IRQ_TYPE_MSI);
319e8817de7SGustavo Pimentel 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, msi_num);
3202c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
3214c50f933SDamien Le Moal 				 msix ? COMMAND_RAISE_MSIX_IRQ :
3224c50f933SDamien Le Moal 				 COMMAND_RAISE_MSI_IRQ);
3232c156ac7SKishon Vijay Abraham I 	val = wait_for_completion_timeout(&test->irq_raised,
3242c156ac7SKishon Vijay Abraham I 					  msecs_to_jiffies(1000));
3252c156ac7SKishon Vijay Abraham I 	if (!val)
3262c156ac7SKishon Vijay Abraham I 		return false;
3272c156ac7SKishon Vijay Abraham I 
3284c50f933SDamien Le Moal 	return pci_irq_vector(pdev, msi_num - 1) == test->last_irq;
3292c156ac7SKishon Vijay Abraham I }
3302c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_validate_xfer_params(struct device * dev,struct pci_endpoint_test_xfer_param * param,size_t alignment)3313e42deaaSShunsuke Mie static int pci_endpoint_test_validate_xfer_params(struct device *dev,
3323e42deaaSShunsuke Mie 		struct pci_endpoint_test_xfer_param *param, size_t alignment)
3333e42deaaSShunsuke Mie {
3348e30538eSShunsuke Mie 	if (!param->size) {
3358e30538eSShunsuke Mie 		dev_dbg(dev, "Data size is zero\n");
3368e30538eSShunsuke Mie 		return -EINVAL;
3378e30538eSShunsuke Mie 	}
3388e30538eSShunsuke Mie 
3393e42deaaSShunsuke Mie 	if (param->size > SIZE_MAX - alignment) {
3403e42deaaSShunsuke Mie 		dev_dbg(dev, "Maximum transfer data size exceeded\n");
3413e42deaaSShunsuke Mie 		return -EINVAL;
3423e42deaaSShunsuke Mie 	}
3433e42deaaSShunsuke Mie 
3443e42deaaSShunsuke Mie 	return 0;
3453e42deaaSShunsuke Mie }
3463e42deaaSShunsuke Mie 
pci_endpoint_test_copy(struct pci_endpoint_test * test,unsigned long arg)347cf376b4bSKishon Vijay Abraham I static bool pci_endpoint_test_copy(struct pci_endpoint_test *test,
348cf376b4bSKishon Vijay Abraham I 				   unsigned long arg)
3492c156ac7SKishon Vijay Abraham I {
350cf376b4bSKishon Vijay Abraham I 	struct pci_endpoint_test_xfer_param param;
3512c156ac7SKishon Vijay Abraham I 	bool ret = false;
3522c156ac7SKishon Vijay Abraham I 	void *src_addr;
3532c156ac7SKishon Vijay Abraham I 	void *dst_addr;
354cf376b4bSKishon Vijay Abraham I 	u32 flags = 0;
355cf376b4bSKishon Vijay Abraham I 	bool use_dma;
356cf376b4bSKishon Vijay Abraham I 	size_t size;
3572c156ac7SKishon Vijay Abraham I 	dma_addr_t src_phys_addr;
3582c156ac7SKishon Vijay Abraham I 	dma_addr_t dst_phys_addr;
3592c156ac7SKishon Vijay Abraham I 	struct pci_dev *pdev = test->pdev;
3602c156ac7SKishon Vijay Abraham I 	struct device *dev = &pdev->dev;
36113107c60SKishon Vijay Abraham I 	void *orig_src_addr;
36213107c60SKishon Vijay Abraham I 	dma_addr_t orig_src_phys_addr;
36313107c60SKishon Vijay Abraham I 	void *orig_dst_addr;
36413107c60SKishon Vijay Abraham I 	dma_addr_t orig_dst_phys_addr;
36513107c60SKishon Vijay Abraham I 	size_t offset;
36613107c60SKishon Vijay Abraham I 	size_t alignment = test->alignment;
367b2ba9225SKishon Vijay Abraham I 	int irq_type = test->irq_type;
3682c156ac7SKishon Vijay Abraham I 	u32 src_crc32;
3692c156ac7SKishon Vijay Abraham I 	u32 dst_crc32;
370cf376b4bSKishon Vijay Abraham I 	int err;
3712c156ac7SKishon Vijay Abraham I 
372cf376b4bSKishon Vijay Abraham I 	err = copy_from_user(&param, (void __user *)arg, sizeof(param));
373cf376b4bSKishon Vijay Abraham I 	if (err) {
374cf376b4bSKishon Vijay Abraham I 		dev_err(dev, "Failed to get transfer param\n");
375cf376b4bSKishon Vijay Abraham I 		return false;
376cf376b4bSKishon Vijay Abraham I 	}
377cf376b4bSKishon Vijay Abraham I 
3783e42deaaSShunsuke Mie 	err = pci_endpoint_test_validate_xfer_params(dev, &param, alignment);
3793e42deaaSShunsuke Mie 	if (err)
3803e42deaaSShunsuke Mie 		return false;
3813e42deaaSShunsuke Mie 
382cf376b4bSKishon Vijay Abraham I 	size = param.size;
383343dc693SDan Carpenter 
384cf376b4bSKishon Vijay Abraham I 	use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA);
385cf376b4bSKishon Vijay Abraham I 	if (use_dma)
386cf376b4bSKishon Vijay Abraham I 		flags |= FLAG_USE_DMA;
387cf376b4bSKishon Vijay Abraham I 
388e0332712SGustavo Pimentel 	if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
389e0332712SGustavo Pimentel 		dev_err(dev, "Invalid IRQ type option\n");
390e0332712SGustavo Pimentel 		goto err;
391e0332712SGustavo Pimentel 	}
392e0332712SGustavo Pimentel 
3930a121f9bSKishon Vijay Abraham I 	orig_src_addr = kzalloc(size + alignment, GFP_KERNEL);
39413107c60SKishon Vijay Abraham I 	if (!orig_src_addr) {
3950e52ea61SGustavo Pimentel 		dev_err(dev, "Failed to allocate source buffer\n");
3962c156ac7SKishon Vijay Abraham I 		ret = false;
3972c156ac7SKishon Vijay Abraham I 		goto err;
3982c156ac7SKishon Vijay Abraham I 	}
3992c156ac7SKishon Vijay Abraham I 
4000a121f9bSKishon Vijay Abraham I 	get_random_bytes(orig_src_addr, size + alignment);
4010a121f9bSKishon Vijay Abraham I 	orig_src_phys_addr = dma_map_single(dev, orig_src_addr,
4020a121f9bSKishon Vijay Abraham I 					    size + alignment, DMA_TO_DEVICE);
4030a121f9bSKishon Vijay Abraham I 	if (dma_mapping_error(dev, orig_src_phys_addr)) {
4040a121f9bSKishon Vijay Abraham I 		dev_err(dev, "failed to map source buffer address\n");
4050a121f9bSKishon Vijay Abraham I 		ret = false;
4060a121f9bSKishon Vijay Abraham I 		goto err_src_phys_addr;
4070a121f9bSKishon Vijay Abraham I 	}
4080a121f9bSKishon Vijay Abraham I 
40913107c60SKishon Vijay Abraham I 	if (alignment && !IS_ALIGNED(orig_src_phys_addr, alignment)) {
41013107c60SKishon Vijay Abraham I 		src_phys_addr = PTR_ALIGN(orig_src_phys_addr, alignment);
41113107c60SKishon Vijay Abraham I 		offset = src_phys_addr - orig_src_phys_addr;
41213107c60SKishon Vijay Abraham I 		src_addr = orig_src_addr + offset;
41313107c60SKishon Vijay Abraham I 	} else {
41413107c60SKishon Vijay Abraham I 		src_phys_addr = orig_src_phys_addr;
41513107c60SKishon Vijay Abraham I 		src_addr = orig_src_addr;
41613107c60SKishon Vijay Abraham I 	}
41713107c60SKishon Vijay Abraham I 
4182c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_SRC_ADDR,
4192c156ac7SKishon Vijay Abraham I 				 lower_32_bits(src_phys_addr));
4202c156ac7SKishon Vijay Abraham I 
4212c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR,
4222c156ac7SKishon Vijay Abraham I 				 upper_32_bits(src_phys_addr));
4232c156ac7SKishon Vijay Abraham I 
4242c156ac7SKishon Vijay Abraham I 	src_crc32 = crc32_le(~0, src_addr, size);
4252c156ac7SKishon Vijay Abraham I 
4260a121f9bSKishon Vijay Abraham I 	orig_dst_addr = kzalloc(size + alignment, GFP_KERNEL);
42713107c60SKishon Vijay Abraham I 	if (!orig_dst_addr) {
4280e52ea61SGustavo Pimentel 		dev_err(dev, "Failed to allocate destination address\n");
4292c156ac7SKishon Vijay Abraham I 		ret = false;
4300a121f9bSKishon Vijay Abraham I 		goto err_dst_addr;
4310a121f9bSKishon Vijay Abraham I 	}
4320a121f9bSKishon Vijay Abraham I 
4330a121f9bSKishon Vijay Abraham I 	orig_dst_phys_addr = dma_map_single(dev, orig_dst_addr,
4340a121f9bSKishon Vijay Abraham I 					    size + alignment, DMA_FROM_DEVICE);
4350a121f9bSKishon Vijay Abraham I 	if (dma_mapping_error(dev, orig_dst_phys_addr)) {
4360a121f9bSKishon Vijay Abraham I 		dev_err(dev, "failed to map destination buffer address\n");
4370a121f9bSKishon Vijay Abraham I 		ret = false;
4380a121f9bSKishon Vijay Abraham I 		goto err_dst_phys_addr;
43913107c60SKishon Vijay Abraham I 	}
44013107c60SKishon Vijay Abraham I 
44113107c60SKishon Vijay Abraham I 	if (alignment && !IS_ALIGNED(orig_dst_phys_addr, alignment)) {
44213107c60SKishon Vijay Abraham I 		dst_phys_addr = PTR_ALIGN(orig_dst_phys_addr, alignment);
44313107c60SKishon Vijay Abraham I 		offset = dst_phys_addr - orig_dst_phys_addr;
44413107c60SKishon Vijay Abraham I 		dst_addr = orig_dst_addr + offset;
44513107c60SKishon Vijay Abraham I 	} else {
44613107c60SKishon Vijay Abraham I 		dst_phys_addr = orig_dst_phys_addr;
44713107c60SKishon Vijay Abraham I 		dst_addr = orig_dst_addr;
4482c156ac7SKishon Vijay Abraham I 	}
4492c156ac7SKishon Vijay Abraham I 
4502c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_DST_ADDR,
4512c156ac7SKishon Vijay Abraham I 				 lower_32_bits(dst_phys_addr));
4522c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_DST_ADDR,
4532c156ac7SKishon Vijay Abraham I 				 upper_32_bits(dst_phys_addr));
4542c156ac7SKishon Vijay Abraham I 
4552c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE,
4562c156ac7SKishon Vijay Abraham I 				 size);
4572c156ac7SKishon Vijay Abraham I 
458cf376b4bSKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_FLAGS, flags);
4599133e394SGustavo Pimentel 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
460e8817de7SGustavo Pimentel 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
4612c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
462e8817de7SGustavo Pimentel 				 COMMAND_COPY);
4632c156ac7SKishon Vijay Abraham I 
4642c156ac7SKishon Vijay Abraham I 	wait_for_completion(&test->irq_raised);
4652c156ac7SKishon Vijay Abraham I 
4660a121f9bSKishon Vijay Abraham I 	dma_unmap_single(dev, orig_dst_phys_addr, size + alignment,
4670a121f9bSKishon Vijay Abraham I 			 DMA_FROM_DEVICE);
4680a121f9bSKishon Vijay Abraham I 
4692c156ac7SKishon Vijay Abraham I 	dst_crc32 = crc32_le(~0, dst_addr, size);
4702c156ac7SKishon Vijay Abraham I 	if (dst_crc32 == src_crc32)
4712c156ac7SKishon Vijay Abraham I 		ret = true;
4722c156ac7SKishon Vijay Abraham I 
4730a121f9bSKishon Vijay Abraham I err_dst_phys_addr:
4740a121f9bSKishon Vijay Abraham I 	kfree(orig_dst_addr);
4752c156ac7SKishon Vijay Abraham I 
4760a121f9bSKishon Vijay Abraham I err_dst_addr:
4770a121f9bSKishon Vijay Abraham I 	dma_unmap_single(dev, orig_src_phys_addr, size + alignment,
4780a121f9bSKishon Vijay Abraham I 			 DMA_TO_DEVICE);
4790a121f9bSKishon Vijay Abraham I 
4800a121f9bSKishon Vijay Abraham I err_src_phys_addr:
4810a121f9bSKishon Vijay Abraham I 	kfree(orig_src_addr);
4822c156ac7SKishon Vijay Abraham I 
4832c156ac7SKishon Vijay Abraham I err:
4842c156ac7SKishon Vijay Abraham I 	return ret;
4852c156ac7SKishon Vijay Abraham I }
4862c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_write(struct pci_endpoint_test * test,unsigned long arg)487cf376b4bSKishon Vijay Abraham I static bool pci_endpoint_test_write(struct pci_endpoint_test *test,
488cf376b4bSKishon Vijay Abraham I 				    unsigned long arg)
4892c156ac7SKishon Vijay Abraham I {
490cf376b4bSKishon Vijay Abraham I 	struct pci_endpoint_test_xfer_param param;
4912c156ac7SKishon Vijay Abraham I 	bool ret = false;
492cf376b4bSKishon Vijay Abraham I 	u32 flags = 0;
493cf376b4bSKishon Vijay Abraham I 	bool use_dma;
4942c156ac7SKishon Vijay Abraham I 	u32 reg;
4952c156ac7SKishon Vijay Abraham I 	void *addr;
4962c156ac7SKishon Vijay Abraham I 	dma_addr_t phys_addr;
4972c156ac7SKishon Vijay Abraham I 	struct pci_dev *pdev = test->pdev;
4982c156ac7SKishon Vijay Abraham I 	struct device *dev = &pdev->dev;
49913107c60SKishon Vijay Abraham I 	void *orig_addr;
50013107c60SKishon Vijay Abraham I 	dma_addr_t orig_phys_addr;
50113107c60SKishon Vijay Abraham I 	size_t offset;
50213107c60SKishon Vijay Abraham I 	size_t alignment = test->alignment;
503b2ba9225SKishon Vijay Abraham I 	int irq_type = test->irq_type;
504cf376b4bSKishon Vijay Abraham I 	size_t size;
5052c156ac7SKishon Vijay Abraham I 	u32 crc32;
506cf376b4bSKishon Vijay Abraham I 	int err;
5072c156ac7SKishon Vijay Abraham I 
508cf376b4bSKishon Vijay Abraham I 	err = copy_from_user(&param, (void __user *)arg, sizeof(param));
509cf376b4bSKishon Vijay Abraham I 	if (err != 0) {
510cf376b4bSKishon Vijay Abraham I 		dev_err(dev, "Failed to get transfer param\n");
511cf376b4bSKishon Vijay Abraham I 		return false;
512cf376b4bSKishon Vijay Abraham I 	}
513cf376b4bSKishon Vijay Abraham I 
5143e42deaaSShunsuke Mie 	err = pci_endpoint_test_validate_xfer_params(dev, &param, alignment);
5153e42deaaSShunsuke Mie 	if (err)
5163e42deaaSShunsuke Mie 		return false;
5173e42deaaSShunsuke Mie 
518cf376b4bSKishon Vijay Abraham I 	size = param.size;
519343dc693SDan Carpenter 
520cf376b4bSKishon Vijay Abraham I 	use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA);
521cf376b4bSKishon Vijay Abraham I 	if (use_dma)
522cf376b4bSKishon Vijay Abraham I 		flags |= FLAG_USE_DMA;
523cf376b4bSKishon Vijay Abraham I 
524e0332712SGustavo Pimentel 	if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
525e0332712SGustavo Pimentel 		dev_err(dev, "Invalid IRQ type option\n");
526e0332712SGustavo Pimentel 		goto err;
527e0332712SGustavo Pimentel 	}
528e0332712SGustavo Pimentel 
5290a121f9bSKishon Vijay Abraham I 	orig_addr = kzalloc(size + alignment, GFP_KERNEL);
53013107c60SKishon Vijay Abraham I 	if (!orig_addr) {
5310e52ea61SGustavo Pimentel 		dev_err(dev, "Failed to allocate address\n");
5322c156ac7SKishon Vijay Abraham I 		ret = false;
5332c156ac7SKishon Vijay Abraham I 		goto err;
5342c156ac7SKishon Vijay Abraham I 	}
5352c156ac7SKishon Vijay Abraham I 
5360a121f9bSKishon Vijay Abraham I 	get_random_bytes(orig_addr, size + alignment);
5370a121f9bSKishon Vijay Abraham I 
5380a121f9bSKishon Vijay Abraham I 	orig_phys_addr = dma_map_single(dev, orig_addr, size + alignment,
5390a121f9bSKishon Vijay Abraham I 					DMA_TO_DEVICE);
5400a121f9bSKishon Vijay Abraham I 	if (dma_mapping_error(dev, orig_phys_addr)) {
5410a121f9bSKishon Vijay Abraham I 		dev_err(dev, "failed to map source buffer address\n");
5420a121f9bSKishon Vijay Abraham I 		ret = false;
5430a121f9bSKishon Vijay Abraham I 		goto err_phys_addr;
5440a121f9bSKishon Vijay Abraham I 	}
5450a121f9bSKishon Vijay Abraham I 
54613107c60SKishon Vijay Abraham I 	if (alignment && !IS_ALIGNED(orig_phys_addr, alignment)) {
54713107c60SKishon Vijay Abraham I 		phys_addr =  PTR_ALIGN(orig_phys_addr, alignment);
54813107c60SKishon Vijay Abraham I 		offset = phys_addr - orig_phys_addr;
54913107c60SKishon Vijay Abraham I 		addr = orig_addr + offset;
55013107c60SKishon Vijay Abraham I 	} else {
55113107c60SKishon Vijay Abraham I 		phys_addr = orig_phys_addr;
55213107c60SKishon Vijay Abraham I 		addr = orig_addr;
55313107c60SKishon Vijay Abraham I 	}
55413107c60SKishon Vijay Abraham I 
5552c156ac7SKishon Vijay Abraham I 	crc32 = crc32_le(~0, addr, size);
5562c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_CHECKSUM,
5572c156ac7SKishon Vijay Abraham I 				 crc32);
5582c156ac7SKishon Vijay Abraham I 
5592c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_SRC_ADDR,
5602c156ac7SKishon Vijay Abraham I 				 lower_32_bits(phys_addr));
5612c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR,
5622c156ac7SKishon Vijay Abraham I 				 upper_32_bits(phys_addr));
5632c156ac7SKishon Vijay Abraham I 
5642c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
5652c156ac7SKishon Vijay Abraham I 
566cf376b4bSKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_FLAGS, flags);
5679133e394SGustavo Pimentel 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
568e8817de7SGustavo Pimentel 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
5692c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
570e8817de7SGustavo Pimentel 				 COMMAND_READ);
5712c156ac7SKishon Vijay Abraham I 
5722c156ac7SKishon Vijay Abraham I 	wait_for_completion(&test->irq_raised);
5732c156ac7SKishon Vijay Abraham I 
5742c156ac7SKishon Vijay Abraham I 	reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
5752c156ac7SKishon Vijay Abraham I 	if (reg & STATUS_READ_SUCCESS)
5762c156ac7SKishon Vijay Abraham I 		ret = true;
5772c156ac7SKishon Vijay Abraham I 
5780a121f9bSKishon Vijay Abraham I 	dma_unmap_single(dev, orig_phys_addr, size + alignment,
5790a121f9bSKishon Vijay Abraham I 			 DMA_TO_DEVICE);
5800a121f9bSKishon Vijay Abraham I 
5810a121f9bSKishon Vijay Abraham I err_phys_addr:
5820a121f9bSKishon Vijay Abraham I 	kfree(orig_addr);
5832c156ac7SKishon Vijay Abraham I 
5842c156ac7SKishon Vijay Abraham I err:
5852c156ac7SKishon Vijay Abraham I 	return ret;
5862c156ac7SKishon Vijay Abraham I }
5872c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_read(struct pci_endpoint_test * test,unsigned long arg)588cf376b4bSKishon Vijay Abraham I static bool pci_endpoint_test_read(struct pci_endpoint_test *test,
589cf376b4bSKishon Vijay Abraham I 				   unsigned long arg)
5902c156ac7SKishon Vijay Abraham I {
591cf376b4bSKishon Vijay Abraham I 	struct pci_endpoint_test_xfer_param param;
5922c156ac7SKishon Vijay Abraham I 	bool ret = false;
593cf376b4bSKishon Vijay Abraham I 	u32 flags = 0;
594cf376b4bSKishon Vijay Abraham I 	bool use_dma;
595cf376b4bSKishon Vijay Abraham I 	size_t size;
5962c156ac7SKishon Vijay Abraham I 	void *addr;
5972c156ac7SKishon Vijay Abraham I 	dma_addr_t phys_addr;
5982c156ac7SKishon Vijay Abraham I 	struct pci_dev *pdev = test->pdev;
5992c156ac7SKishon Vijay Abraham I 	struct device *dev = &pdev->dev;
60013107c60SKishon Vijay Abraham I 	void *orig_addr;
60113107c60SKishon Vijay Abraham I 	dma_addr_t orig_phys_addr;
60213107c60SKishon Vijay Abraham I 	size_t offset;
60313107c60SKishon Vijay Abraham I 	size_t alignment = test->alignment;
604b2ba9225SKishon Vijay Abraham I 	int irq_type = test->irq_type;
6052c156ac7SKishon Vijay Abraham I 	u32 crc32;
606cf376b4bSKishon Vijay Abraham I 	int err;
6072c156ac7SKishon Vijay Abraham I 
608cf376b4bSKishon Vijay Abraham I 	err = copy_from_user(&param, (void __user *)arg, sizeof(param));
609cf376b4bSKishon Vijay Abraham I 	if (err) {
610cf376b4bSKishon Vijay Abraham I 		dev_err(dev, "Failed to get transfer param\n");
611cf376b4bSKishon Vijay Abraham I 		return false;
612cf376b4bSKishon Vijay Abraham I 	}
613cf376b4bSKishon Vijay Abraham I 
6143e42deaaSShunsuke Mie 	err = pci_endpoint_test_validate_xfer_params(dev, &param, alignment);
6153e42deaaSShunsuke Mie 	if (err)
6163e42deaaSShunsuke Mie 		return false;
6173e42deaaSShunsuke Mie 
618cf376b4bSKishon Vijay Abraham I 	size = param.size;
619343dc693SDan Carpenter 
620cf376b4bSKishon Vijay Abraham I 	use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA);
621cf376b4bSKishon Vijay Abraham I 	if (use_dma)
622cf376b4bSKishon Vijay Abraham I 		flags |= FLAG_USE_DMA;
623cf376b4bSKishon Vijay Abraham I 
624e0332712SGustavo Pimentel 	if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
625e0332712SGustavo Pimentel 		dev_err(dev, "Invalid IRQ type option\n");
626e0332712SGustavo Pimentel 		goto err;
627e0332712SGustavo Pimentel 	}
628e0332712SGustavo Pimentel 
6290a121f9bSKishon Vijay Abraham I 	orig_addr = kzalloc(size + alignment, GFP_KERNEL);
63013107c60SKishon Vijay Abraham I 	if (!orig_addr) {
6310e52ea61SGustavo Pimentel 		dev_err(dev, "Failed to allocate destination address\n");
6322c156ac7SKishon Vijay Abraham I 		ret = false;
6332c156ac7SKishon Vijay Abraham I 		goto err;
6342c156ac7SKishon Vijay Abraham I 	}
6352c156ac7SKishon Vijay Abraham I 
6360a121f9bSKishon Vijay Abraham I 	orig_phys_addr = dma_map_single(dev, orig_addr, size + alignment,
6370a121f9bSKishon Vijay Abraham I 					DMA_FROM_DEVICE);
6380a121f9bSKishon Vijay Abraham I 	if (dma_mapping_error(dev, orig_phys_addr)) {
6390a121f9bSKishon Vijay Abraham I 		dev_err(dev, "failed to map source buffer address\n");
6400a121f9bSKishon Vijay Abraham I 		ret = false;
6410a121f9bSKishon Vijay Abraham I 		goto err_phys_addr;
6420a121f9bSKishon Vijay Abraham I 	}
6430a121f9bSKishon Vijay Abraham I 
64413107c60SKishon Vijay Abraham I 	if (alignment && !IS_ALIGNED(orig_phys_addr, alignment)) {
64513107c60SKishon Vijay Abraham I 		phys_addr = PTR_ALIGN(orig_phys_addr, alignment);
64613107c60SKishon Vijay Abraham I 		offset = phys_addr - orig_phys_addr;
64713107c60SKishon Vijay Abraham I 		addr = orig_addr + offset;
64813107c60SKishon Vijay Abraham I 	} else {
64913107c60SKishon Vijay Abraham I 		phys_addr = orig_phys_addr;
65013107c60SKishon Vijay Abraham I 		addr = orig_addr;
65113107c60SKishon Vijay Abraham I 	}
65213107c60SKishon Vijay Abraham I 
6532c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_DST_ADDR,
6542c156ac7SKishon Vijay Abraham I 				 lower_32_bits(phys_addr));
6552c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_DST_ADDR,
6562c156ac7SKishon Vijay Abraham I 				 upper_32_bits(phys_addr));
6572c156ac7SKishon Vijay Abraham I 
6582c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
6592c156ac7SKishon Vijay Abraham I 
660cf376b4bSKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_FLAGS, flags);
6619133e394SGustavo Pimentel 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
662e8817de7SGustavo Pimentel 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
6632c156ac7SKishon Vijay Abraham I 	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
664e8817de7SGustavo Pimentel 				 COMMAND_WRITE);
6652c156ac7SKishon Vijay Abraham I 
6662c156ac7SKishon Vijay Abraham I 	wait_for_completion(&test->irq_raised);
6672c156ac7SKishon Vijay Abraham I 
6680a121f9bSKishon Vijay Abraham I 	dma_unmap_single(dev, orig_phys_addr, size + alignment,
6690a121f9bSKishon Vijay Abraham I 			 DMA_FROM_DEVICE);
6700a121f9bSKishon Vijay Abraham I 
6712c156ac7SKishon Vijay Abraham I 	crc32 = crc32_le(~0, addr, size);
6722c156ac7SKishon Vijay Abraham I 	if (crc32 == pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM))
6732c156ac7SKishon Vijay Abraham I 		ret = true;
6742c156ac7SKishon Vijay Abraham I 
6750a121f9bSKishon Vijay Abraham I err_phys_addr:
6760a121f9bSKishon Vijay Abraham I 	kfree(orig_addr);
6772c156ac7SKishon Vijay Abraham I err:
6782c156ac7SKishon Vijay Abraham I 	return ret;
6792c156ac7SKishon Vijay Abraham I }
6802c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_clear_irq(struct pci_endpoint_test * test)681475007f9SKishon Vijay Abraham I static bool pci_endpoint_test_clear_irq(struct pci_endpoint_test *test)
682475007f9SKishon Vijay Abraham I {
683475007f9SKishon Vijay Abraham I 	pci_endpoint_test_release_irq(test);
684475007f9SKishon Vijay Abraham I 	pci_endpoint_test_free_irq_vectors(test);
685475007f9SKishon Vijay Abraham I 	return true;
686475007f9SKishon Vijay Abraham I }
687475007f9SKishon Vijay Abraham I 
pci_endpoint_test_set_irq(struct pci_endpoint_test * test,int req_irq_type)688e0332712SGustavo Pimentel static bool pci_endpoint_test_set_irq(struct pci_endpoint_test *test,
689e0332712SGustavo Pimentel 				      int req_irq_type)
690e0332712SGustavo Pimentel {
691e0332712SGustavo Pimentel 	struct pci_dev *pdev = test->pdev;
692e0332712SGustavo Pimentel 	struct device *dev = &pdev->dev;
693e0332712SGustavo Pimentel 
694e0332712SGustavo Pimentel 	if (req_irq_type < IRQ_TYPE_LEGACY || req_irq_type > IRQ_TYPE_MSIX) {
695e0332712SGustavo Pimentel 		dev_err(dev, "Invalid IRQ type option\n");
696e0332712SGustavo Pimentel 		return false;
697e0332712SGustavo Pimentel 	}
698e0332712SGustavo Pimentel 
699b2ba9225SKishon Vijay Abraham I 	if (test->irq_type == req_irq_type)
700e0332712SGustavo Pimentel 		return true;
701e0332712SGustavo Pimentel 
702e0332712SGustavo Pimentel 	pci_endpoint_test_release_irq(test);
703e0332712SGustavo Pimentel 	pci_endpoint_test_free_irq_vectors(test);
704e0332712SGustavo Pimentel 
705e0332712SGustavo Pimentel 	if (!pci_endpoint_test_alloc_irq_vectors(test, req_irq_type))
706e0332712SGustavo Pimentel 		goto err;
707e0332712SGustavo Pimentel 
708e0332712SGustavo Pimentel 	if (!pci_endpoint_test_request_irq(test))
709e0332712SGustavo Pimentel 		goto err;
710e0332712SGustavo Pimentel 
711e0332712SGustavo Pimentel 	return true;
712e0332712SGustavo Pimentel 
713e0332712SGustavo Pimentel err:
714e0332712SGustavo Pimentel 	pci_endpoint_test_free_irq_vectors(test);
715e0332712SGustavo Pimentel 	return false;
716e0332712SGustavo Pimentel }
717e0332712SGustavo Pimentel 
pci_endpoint_test_ioctl(struct file * file,unsigned int cmd,unsigned long arg)7182c156ac7SKishon Vijay Abraham I static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
7192c156ac7SKishon Vijay Abraham I 				    unsigned long arg)
7202c156ac7SKishon Vijay Abraham I {
7212c156ac7SKishon Vijay Abraham I 	int ret = -EINVAL;
7222c156ac7SKishon Vijay Abraham I 	enum pci_barno bar;
7232c156ac7SKishon Vijay Abraham I 	struct pci_endpoint_test *test = to_endpoint_test(file->private_data);
7245bb04b19SKishon Vijay Abraham I 	struct pci_dev *pdev = test->pdev;
7252c156ac7SKishon Vijay Abraham I 
7262c156ac7SKishon Vijay Abraham I 	mutex_lock(&test->mutex);
727fb620ae7SDamien Le Moal 
728fb620ae7SDamien Le Moal 	reinit_completion(&test->irq_raised);
729fb620ae7SDamien Le Moal 	test->last_irq = -ENODATA;
730fb620ae7SDamien Le Moal 
7312c156ac7SKishon Vijay Abraham I 	switch (cmd) {
7322c156ac7SKishon Vijay Abraham I 	case PCITEST_BAR:
7332c156ac7SKishon Vijay Abraham I 		bar = arg;
73433fcc549SGustavo Pimentel 		if (bar > BAR_5)
7352c156ac7SKishon Vijay Abraham I 			goto ret;
7365bb04b19SKishon Vijay Abraham I 		if (is_am654_pci_dev(pdev) && bar == BAR_0)
7375bb04b19SKishon Vijay Abraham I 			goto ret;
7382c156ac7SKishon Vijay Abraham I 		ret = pci_endpoint_test_bar(test, bar);
7392c156ac7SKishon Vijay Abraham I 		break;
7402c156ac7SKishon Vijay Abraham I 	case PCITEST_LEGACY_IRQ:
7412c156ac7SKishon Vijay Abraham I 		ret = pci_endpoint_test_legacy_irq(test);
7422c156ac7SKishon Vijay Abraham I 		break;
7432c156ac7SKishon Vijay Abraham I 	case PCITEST_MSI:
744c2e00e31SGustavo Pimentel 	case PCITEST_MSIX:
745c2e00e31SGustavo Pimentel 		ret = pci_endpoint_test_msi_irq(test, arg, cmd == PCITEST_MSIX);
7462c156ac7SKishon Vijay Abraham I 		break;
7472c156ac7SKishon Vijay Abraham I 	case PCITEST_WRITE:
7482c156ac7SKishon Vijay Abraham I 		ret = pci_endpoint_test_write(test, arg);
7492c156ac7SKishon Vijay Abraham I 		break;
7502c156ac7SKishon Vijay Abraham I 	case PCITEST_READ:
7512c156ac7SKishon Vijay Abraham I 		ret = pci_endpoint_test_read(test, arg);
7522c156ac7SKishon Vijay Abraham I 		break;
7532c156ac7SKishon Vijay Abraham I 	case PCITEST_COPY:
7542c156ac7SKishon Vijay Abraham I 		ret = pci_endpoint_test_copy(test, arg);
7552c156ac7SKishon Vijay Abraham I 		break;
756e0332712SGustavo Pimentel 	case PCITEST_SET_IRQTYPE:
757e0332712SGustavo Pimentel 		ret = pci_endpoint_test_set_irq(test, arg);
758e0332712SGustavo Pimentel 		break;
759e0332712SGustavo Pimentel 	case PCITEST_GET_IRQTYPE:
760e0332712SGustavo Pimentel 		ret = irq_type;
761e0332712SGustavo Pimentel 		break;
762475007f9SKishon Vijay Abraham I 	case PCITEST_CLEAR_IRQ:
763475007f9SKishon Vijay Abraham I 		ret = pci_endpoint_test_clear_irq(test);
764475007f9SKishon Vijay Abraham I 		break;
7652c156ac7SKishon Vijay Abraham I 	}
7662c156ac7SKishon Vijay Abraham I 
7672c156ac7SKishon Vijay Abraham I ret:
7682c156ac7SKishon Vijay Abraham I 	mutex_unlock(&test->mutex);
7692c156ac7SKishon Vijay Abraham I 	return ret;
7702c156ac7SKishon Vijay Abraham I }
7712c156ac7SKishon Vijay Abraham I 
7722c156ac7SKishon Vijay Abraham I static const struct file_operations pci_endpoint_test_fops = {
7732c156ac7SKishon Vijay Abraham I 	.owner = THIS_MODULE,
7742c156ac7SKishon Vijay Abraham I 	.unlocked_ioctl = pci_endpoint_test_ioctl,
7752c156ac7SKishon Vijay Abraham I };
7762c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_probe(struct pci_dev * pdev,const struct pci_device_id * ent)7772c156ac7SKishon Vijay Abraham I static int pci_endpoint_test_probe(struct pci_dev *pdev,
7782c156ac7SKishon Vijay Abraham I 				   const struct pci_device_id *ent)
7792c156ac7SKishon Vijay Abraham I {
7802c156ac7SKishon Vijay Abraham I 	int err;
7812c156ac7SKishon Vijay Abraham I 	int id;
7826b443e5cSKishon Vijay Abraham I 	char name[24];
7832c156ac7SKishon Vijay Abraham I 	enum pci_barno bar;
7842c156ac7SKishon Vijay Abraham I 	void __iomem *base;
7852c156ac7SKishon Vijay Abraham I 	struct device *dev = &pdev->dev;
7862c156ac7SKishon Vijay Abraham I 	struct pci_endpoint_test *test;
787834b9051SKishon Vijay Abraham I 	struct pci_endpoint_test_data *data;
788834b9051SKishon Vijay Abraham I 	enum pci_barno test_reg_bar = BAR_0;
7892c156ac7SKishon Vijay Abraham I 	struct miscdevice *misc_device;
7902c156ac7SKishon Vijay Abraham I 
7912c156ac7SKishon Vijay Abraham I 	if (pci_is_bridge(pdev))
7922c156ac7SKishon Vijay Abraham I 		return -ENODEV;
7932c156ac7SKishon Vijay Abraham I 
7942c156ac7SKishon Vijay Abraham I 	test = devm_kzalloc(dev, sizeof(*test), GFP_KERNEL);
7952c156ac7SKishon Vijay Abraham I 	if (!test)
7962c156ac7SKishon Vijay Abraham I 		return -ENOMEM;
7972c156ac7SKishon Vijay Abraham I 
798834b9051SKishon Vijay Abraham I 	test->test_reg_bar = 0;
79913107c60SKishon Vijay Abraham I 	test->alignment = 0;
8002c156ac7SKishon Vijay Abraham I 	test->pdev = pdev;
801b2ba9225SKishon Vijay Abraham I 	test->irq_type = IRQ_TYPE_UNDEFINED;
802834b9051SKishon Vijay Abraham I 
8039133e394SGustavo Pimentel 	if (no_msi)
8049133e394SGustavo Pimentel 		irq_type = IRQ_TYPE_LEGACY;
8059133e394SGustavo Pimentel 
806834b9051SKishon Vijay Abraham I 	data = (struct pci_endpoint_test_data *)ent->driver_data;
80713107c60SKishon Vijay Abraham I 	if (data) {
808834b9051SKishon Vijay Abraham I 		test_reg_bar = data->test_reg_bar;
8098f220664SKishon Vijay Abraham I 		test->test_reg_bar = test_reg_bar;
81013107c60SKishon Vijay Abraham I 		test->alignment = data->alignment;
8119133e394SGustavo Pimentel 		irq_type = data->irq_type;
81213107c60SKishon Vijay Abraham I 	}
813834b9051SKishon Vijay Abraham I 
8142c156ac7SKishon Vijay Abraham I 	init_completion(&test->irq_raised);
8152c156ac7SKishon Vijay Abraham I 	mutex_init(&test->mutex);
8162c156ac7SKishon Vijay Abraham I 
8170a121f9bSKishon Vijay Abraham I 	if ((dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48)) != 0) &&
8180a121f9bSKishon Vijay Abraham I 	    dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)) != 0) {
8190a121f9bSKishon Vijay Abraham I 		dev_err(dev, "Cannot set DMA mask\n");
8200a121f9bSKishon Vijay Abraham I 		return -EINVAL;
8210a121f9bSKishon Vijay Abraham I 	}
8220a121f9bSKishon Vijay Abraham I 
8232c156ac7SKishon Vijay Abraham I 	err = pci_enable_device(pdev);
8242c156ac7SKishon Vijay Abraham I 	if (err) {
8252c156ac7SKishon Vijay Abraham I 		dev_err(dev, "Cannot enable PCI device\n");
8262c156ac7SKishon Vijay Abraham I 		return err;
8272c156ac7SKishon Vijay Abraham I 	}
8282c156ac7SKishon Vijay Abraham I 
8292c156ac7SKishon Vijay Abraham I 	err = pci_request_regions(pdev, DRV_MODULE_NAME);
8302c156ac7SKishon Vijay Abraham I 	if (err) {
8312c156ac7SKishon Vijay Abraham I 		dev_err(dev, "Cannot obtain PCI resources\n");
8322c156ac7SKishon Vijay Abraham I 		goto err_disable_pdev;
8332c156ac7SKishon Vijay Abraham I 	}
8342c156ac7SKishon Vijay Abraham I 
8352c156ac7SKishon Vijay Abraham I 	pci_set_master(pdev);
8362c156ac7SKishon Vijay Abraham I 
8371749c904SXiongfeng Wang 	if (!pci_endpoint_test_alloc_irq_vectors(test, irq_type)) {
8381749c904SXiongfeng Wang 		err = -EINVAL;
839e0332712SGustavo Pimentel 		goto err_disable_irq;
8401749c904SXiongfeng Wang 	}
8412c156ac7SKishon Vijay Abraham I 
842c9c13ba4SDenis Efremov 	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
84316b17cadSNiklas Cassel 		if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
8442c156ac7SKishon Vijay Abraham I 			base = pci_ioremap_bar(pdev, bar);
8452c156ac7SKishon Vijay Abraham I 			if (!base) {
8460e52ea61SGustavo Pimentel 				dev_err(dev, "Failed to read BAR%d\n", bar);
847834b9051SKishon Vijay Abraham I 				WARN_ON(bar == test_reg_bar);
8482c156ac7SKishon Vijay Abraham I 			}
8492c156ac7SKishon Vijay Abraham I 			test->bar[bar] = base;
8502c156ac7SKishon Vijay Abraham I 		}
85116b17cadSNiklas Cassel 	}
8522c156ac7SKishon Vijay Abraham I 
853834b9051SKishon Vijay Abraham I 	test->base = test->bar[test_reg_bar];
8542c156ac7SKishon Vijay Abraham I 	if (!test->base) {
85580068c93SKishon Vijay Abraham I 		err = -ENOMEM;
856834b9051SKishon Vijay Abraham I 		dev_err(dev, "Cannot perform PCI test without BAR%d\n",
857834b9051SKishon Vijay Abraham I 			test_reg_bar);
8582c156ac7SKishon Vijay Abraham I 		goto err_iounmap;
8592c156ac7SKishon Vijay Abraham I 	}
8602c156ac7SKishon Vijay Abraham I 
8612c156ac7SKishon Vijay Abraham I 	pci_set_drvdata(pdev, test);
8622c156ac7SKishon Vijay Abraham I 
8632c156ac7SKishon Vijay Abraham I 	id = ida_simple_get(&pci_endpoint_test_ida, 0, 0, GFP_KERNEL);
8642c156ac7SKishon Vijay Abraham I 	if (id < 0) {
86580068c93SKishon Vijay Abraham I 		err = id;
8660e52ea61SGustavo Pimentel 		dev_err(dev, "Unable to get id\n");
8672c156ac7SKishon Vijay Abraham I 		goto err_iounmap;
8682c156ac7SKishon Vijay Abraham I 	}
8692c156ac7SKishon Vijay Abraham I 
8702c156ac7SKishon Vijay Abraham I 	snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id);
871c2be14abSKishon Vijay Abraham I 	test->name = kstrdup(name, GFP_KERNEL);
872c2be14abSKishon Vijay Abraham I 	if (!test->name) {
873c2be14abSKishon Vijay Abraham I 		err = -ENOMEM;
874c2be14abSKishon Vijay Abraham I 		goto err_ida_remove;
875c2be14abSKishon Vijay Abraham I 	}
876c2be14abSKishon Vijay Abraham I 
8771749c904SXiongfeng Wang 	if (!pci_endpoint_test_request_irq(test)) {
8781749c904SXiongfeng Wang 		err = -EINVAL;
879c2be14abSKishon Vijay Abraham I 		goto err_kfree_test_name;
8801749c904SXiongfeng Wang 	}
881c2be14abSKishon Vijay Abraham I 
8822c156ac7SKishon Vijay Abraham I 	misc_device = &test->miscdev;
8832c156ac7SKishon Vijay Abraham I 	misc_device->minor = MISC_DYNAMIC_MINOR;
884139838ffSKishon Vijay Abraham I 	misc_device->name = kstrdup(name, GFP_KERNEL);
885139838ffSKishon Vijay Abraham I 	if (!misc_device->name) {
886139838ffSKishon Vijay Abraham I 		err = -ENOMEM;
887c2be14abSKishon Vijay Abraham I 		goto err_release_irq;
888139838ffSKishon Vijay Abraham I 	}
88974a03c20SRichard Weinberger 	misc_device->parent = &pdev->dev;
890560dbc46SMing Wang 	misc_device->fops = &pci_endpoint_test_fops;
8912c156ac7SKishon Vijay Abraham I 
8922c156ac7SKishon Vijay Abraham I 	err = misc_register(misc_device);
8932c156ac7SKishon Vijay Abraham I 	if (err) {
8940e52ea61SGustavo Pimentel 		dev_err(dev, "Failed to register device\n");
895139838ffSKishon Vijay Abraham I 		goto err_kfree_name;
8962c156ac7SKishon Vijay Abraham I 	}
8972c156ac7SKishon Vijay Abraham I 
8982c156ac7SKishon Vijay Abraham I 	return 0;
8992c156ac7SKishon Vijay Abraham I 
900139838ffSKishon Vijay Abraham I err_kfree_name:
901139838ffSKishon Vijay Abraham I 	kfree(misc_device->name);
902139838ffSKishon Vijay Abraham I 
903c2be14abSKishon Vijay Abraham I err_release_irq:
904c2be14abSKishon Vijay Abraham I 	pci_endpoint_test_release_irq(test);
905c2be14abSKishon Vijay Abraham I 
906c2be14abSKishon Vijay Abraham I err_kfree_test_name:
907c2be14abSKishon Vijay Abraham I 	kfree(test->name);
908c2be14abSKishon Vijay Abraham I 
9092c156ac7SKishon Vijay Abraham I err_ida_remove:
9102c156ac7SKishon Vijay Abraham I 	ida_simple_remove(&pci_endpoint_test_ida, id);
9112c156ac7SKishon Vijay Abraham I 
9122c156ac7SKishon Vijay Abraham I err_iounmap:
913c9c13ba4SDenis Efremov 	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
9142c156ac7SKishon Vijay Abraham I 		if (test->bar[bar])
9152c156ac7SKishon Vijay Abraham I 			pci_iounmap(pdev, test->bar[bar]);
9162c156ac7SKishon Vijay Abraham I 	}
9172c156ac7SKishon Vijay Abraham I 
918e0332712SGustavo Pimentel err_disable_irq:
919e0332712SGustavo Pimentel 	pci_endpoint_test_free_irq_vectors(test);
9202c156ac7SKishon Vijay Abraham I 	pci_release_regions(pdev);
9212c156ac7SKishon Vijay Abraham I 
9222c156ac7SKishon Vijay Abraham I err_disable_pdev:
9232c156ac7SKishon Vijay Abraham I 	pci_disable_device(pdev);
9242c156ac7SKishon Vijay Abraham I 
9252c156ac7SKishon Vijay Abraham I 	return err;
9262c156ac7SKishon Vijay Abraham I }
9272c156ac7SKishon Vijay Abraham I 
pci_endpoint_test_remove(struct pci_dev * pdev)9282c156ac7SKishon Vijay Abraham I static void pci_endpoint_test_remove(struct pci_dev *pdev)
9292c156ac7SKishon Vijay Abraham I {
9302c156ac7SKishon Vijay Abraham I 	int id;
9312c156ac7SKishon Vijay Abraham I 	enum pci_barno bar;
9322c156ac7SKishon Vijay Abraham I 	struct pci_endpoint_test *test = pci_get_drvdata(pdev);
9332c156ac7SKishon Vijay Abraham I 	struct miscdevice *misc_device = &test->miscdev;
9342c156ac7SKishon Vijay Abraham I 
9352c156ac7SKishon Vijay Abraham I 	if (sscanf(misc_device->name, DRV_MODULE_NAME ".%d", &id) != 1)
9362c156ac7SKishon Vijay Abraham I 		return;
937a2db2663SDan Carpenter 	if (id < 0)
938a2db2663SDan Carpenter 		return;
9392c156ac7SKishon Vijay Abraham I 
940f61b7634SDamien Le Moal 	pci_endpoint_test_release_irq(test);
941f61b7634SDamien Le Moal 	pci_endpoint_test_free_irq_vectors(test);
942f61b7634SDamien Le Moal 
9432c156ac7SKishon Vijay Abraham I 	misc_deregister(&test->miscdev);
944139838ffSKishon Vijay Abraham I 	kfree(misc_device->name);
945c2be14abSKishon Vijay Abraham I 	kfree(test->name);
9462c156ac7SKishon Vijay Abraham I 	ida_simple_remove(&pci_endpoint_test_ida, id);
947c9c13ba4SDenis Efremov 	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
9482c156ac7SKishon Vijay Abraham I 		if (test->bar[bar])
9492c156ac7SKishon Vijay Abraham I 			pci_iounmap(pdev, test->bar[bar]);
9502c156ac7SKishon Vijay Abraham I 	}
951e0332712SGustavo Pimentel 
9522c156ac7SKishon Vijay Abraham I 	pci_release_regions(pdev);
9532c156ac7SKishon Vijay Abraham I 	pci_disable_device(pdev);
9542c156ac7SKishon Vijay Abraham I }
9552c156ac7SKishon Vijay Abraham I 
9560a121f9bSKishon Vijay Abraham I static const struct pci_endpoint_test_data default_data = {
9570a121f9bSKishon Vijay Abraham I 	.test_reg_bar = BAR_0,
9580a121f9bSKishon Vijay Abraham I 	.alignment = SZ_4K,
9590a121f9bSKishon Vijay Abraham I 	.irq_type = IRQ_TYPE_MSI,
9600a121f9bSKishon Vijay Abraham I };
9610a121f9bSKishon Vijay Abraham I 
9625bb04b19SKishon Vijay Abraham I static const struct pci_endpoint_test_data am654_data = {
9635bb04b19SKishon Vijay Abraham I 	.test_reg_bar = BAR_2,
9645bb04b19SKishon Vijay Abraham I 	.alignment = SZ_64K,
9655bb04b19SKishon Vijay Abraham I 	.irq_type = IRQ_TYPE_MSI,
9665bb04b19SKishon Vijay Abraham I };
9675bb04b19SKishon Vijay Abraham I 
9686546ae29SKishon Vijay Abraham I static const struct pci_endpoint_test_data j721e_data = {
9696546ae29SKishon Vijay Abraham I 	.alignment = 256,
9706546ae29SKishon Vijay Abraham I 	.irq_type = IRQ_TYPE_MSI,
9716546ae29SKishon Vijay Abraham I };
9726546ae29SKishon Vijay Abraham I 
9732c156ac7SKishon Vijay Abraham I static const struct pci_device_id pci_endpoint_test_tbl[] = {
9740a121f9bSKishon Vijay Abraham I 	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x),
9750a121f9bSKishon Vijay Abraham I 	  .driver_data = (kernel_ulong_t)&default_data,
9760a121f9bSKishon Vijay Abraham I 	},
9770a121f9bSKishon Vijay Abraham I 	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x),
9780a121f9bSKishon Vijay Abraham I 	  .driver_data = (kernel_ulong_t)&default_data,
9790a121f9bSKishon Vijay Abraham I 	},
98009fb37b3SHou Zhiqiang 	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0),
98109fb37b3SHou Zhiqiang 	  .driver_data = (kernel_ulong_t)&default_data,
98209fb37b3SHou Zhiqiang 	},
98301ea5edeSRichard Zhu 	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8),},
98409fb37b3SHou Zhiqiang 	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_LS1088A),
98509fb37b3SHou Zhiqiang 	  .driver_data = (kernel_ulong_t)&default_data,
98609fb37b3SHou Zhiqiang 	},
9871f418f46SGustavo Pimentel 	{ PCI_DEVICE_DATA(SYNOPSYS, EDDA, NULL) },
9885bb04b19SKishon Vijay Abraham I 	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM654),
9895bb04b19SKishon Vijay Abraham I 	  .driver_data = (kernel_ulong_t)&am654_data
9905bb04b19SKishon Vijay Abraham I 	},
991cfb824ddSLad Prabhakar 	{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774A1),},
992cfb824ddSLad Prabhakar 	{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1),},
993cfb824ddSLad Prabhakar 	{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0),},
994a63c5f3dSLad Prabhakar 	{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1),},
995*f22145b5SYoshihiro Shimoda 	{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A779F0),
996*f22145b5SYoshihiro Shimoda 	  .driver_data = (kernel_ulong_t)&default_data,
997*f22145b5SYoshihiro Shimoda 	},
9986546ae29SKishon Vijay Abraham I 	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721E),
9996546ae29SKishon Vijay Abraham I 	  .driver_data = (kernel_ulong_t)&j721e_data,
10006546ae29SKishon Vijay Abraham I 	},
10017c52009dSKishon Vijay Abraham I 	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J7200),
10027c52009dSKishon Vijay Abraham I 	  .driver_data = (kernel_ulong_t)&j721e_data,
10037c52009dSKishon Vijay Abraham I 	},
10047c52009dSKishon Vijay Abraham I 	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM64),
10057c52009dSKishon Vijay Abraham I 	  .driver_data = (kernel_ulong_t)&j721e_data,
10067c52009dSKishon Vijay Abraham I 	},
1007be3e3ab5SSiddharth Vadapalli 	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721S2),
1008be3e3ab5SSiddharth Vadapalli 	  .driver_data = (kernel_ulong_t)&j721e_data,
1009be3e3ab5SSiddharth Vadapalli 	},
10102c156ac7SKishon Vijay Abraham I 	{ }
10112c156ac7SKishon Vijay Abraham I };
10122c156ac7SKishon Vijay Abraham I MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl);
10132c156ac7SKishon Vijay Abraham I 
10142c156ac7SKishon Vijay Abraham I static struct pci_driver pci_endpoint_test_driver = {
10152c156ac7SKishon Vijay Abraham I 	.name		= DRV_MODULE_NAME,
10162c156ac7SKishon Vijay Abraham I 	.id_table	= pci_endpoint_test_tbl,
10172c156ac7SKishon Vijay Abraham I 	.probe		= pci_endpoint_test_probe,
10182c156ac7SKishon Vijay Abraham I 	.remove		= pci_endpoint_test_remove,
1019489b1f41SKishon Vijay Abraham I 	.sriov_configure = pci_sriov_configure_simple,
10202c156ac7SKishon Vijay Abraham I };
10212c156ac7SKishon Vijay Abraham I module_pci_driver(pci_endpoint_test_driver);
10222c156ac7SKishon Vijay Abraham I 
10232c156ac7SKishon Vijay Abraham I MODULE_DESCRIPTION("PCI ENDPOINT TEST HOST DRIVER");
10242c156ac7SKishon Vijay Abraham I MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
10252c156ac7SKishon Vijay Abraham I MODULE_LICENSE("GPL v2");
1026