blob: 349794cbe1f3e71ed64b09b12739d19cb5a2ab63 [file] [log] [blame]
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +05301/**
2 * Host side test driver to test endpoint functionality
3 *
4 * Copyright (C) 2017 Texas Instruments
5 * Author: Kishon Vijay Abraham I <kishon@ti.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 of
9 * the License as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <linux/crc32.h>
21#include <linux/delay.h>
22#include <linux/fs.h>
23#include <linux/io.h>
24#include <linux/interrupt.h>
25#include <linux/irq.h>
26#include <linux/miscdevice.h>
27#include <linux/module.h>
28#include <linux/mutex.h>
29#include <linux/random.h>
30#include <linux/slab.h>
31#include <linux/pci.h>
32#include <linux/pci_ids.h>
33
34#include <linux/pci_regs.h>
35
36#include <uapi/linux/pcitest.h>
37
Gustavo Pimentele8817de2018-07-19 10:32:17 +020038#define DRV_MODULE_NAME "pci-endpoint-test"
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +053039
Gustavo Pimentele8817de2018-07-19 10:32:17 +020040#define IRQ_TYPE_LEGACY 0
41#define IRQ_TYPE_MSI 1
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +053042
Gustavo Pimentele8817de2018-07-19 10:32:17 +020043#define PCI_ENDPOINT_TEST_MAGIC 0x0
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +053044
Gustavo Pimentele8817de2018-07-19 10:32:17 +020045#define PCI_ENDPOINT_TEST_COMMAND 0x4
46#define COMMAND_RAISE_LEGACY_IRQ BIT(0)
47#define COMMAND_RAISE_MSI_IRQ BIT(1)
48/* BIT(2) is reserved for raising MSI-X IRQ command */
49#define COMMAND_READ BIT(3)
50#define COMMAND_WRITE BIT(4)
51#define COMMAND_COPY BIT(5)
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +053052
Gustavo Pimentele8817de2018-07-19 10:32:17 +020053#define PCI_ENDPOINT_TEST_STATUS 0x8
54#define STATUS_READ_SUCCESS BIT(0)
55#define STATUS_READ_FAIL BIT(1)
56#define STATUS_WRITE_SUCCESS BIT(2)
57#define STATUS_WRITE_FAIL BIT(3)
58#define STATUS_COPY_SUCCESS BIT(4)
59#define STATUS_COPY_FAIL BIT(5)
60#define STATUS_IRQ_RAISED BIT(6)
61#define STATUS_SRC_ADDR_INVALID BIT(7)
62#define STATUS_DST_ADDR_INVALID BIT(8)
63
64#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0x0c
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +053065#define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10
66
67#define PCI_ENDPOINT_TEST_LOWER_DST_ADDR 0x14
68#define PCI_ENDPOINT_TEST_UPPER_DST_ADDR 0x18
69
Gustavo Pimentele8817de2018-07-19 10:32:17 +020070#define PCI_ENDPOINT_TEST_SIZE 0x1c
71#define PCI_ENDPOINT_TEST_CHECKSUM 0x20
72
73#define PCI_ENDPOINT_TEST_IRQ_TYPE 0x24
74#define PCI_ENDPOINT_TEST_IRQ_NUMBER 0x28
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +053075
76static DEFINE_IDA(pci_endpoint_test_ida);
77
78#define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \
79 miscdev)
Kishon Vijay Abraham I0c8a5f92017-08-18 20:28:09 +053080
81static bool no_msi;
82module_param(no_msi, bool, 0444);
83MODULE_PARM_DESC(no_msi, "Disable MSI interrupt in pci_endpoint_test");
84
Gustavo Pimentel9133e392018-07-19 10:32:18 +020085static int irq_type = IRQ_TYPE_MSI;
86module_param(irq_type, int, 0444);
87MODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI)");
88
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +053089enum pci_barno {
90 BAR_0,
91 BAR_1,
92 BAR_2,
93 BAR_3,
94 BAR_4,
95 BAR_5,
96};
97
98struct pci_endpoint_test {
99 struct pci_dev *pdev;
100 void __iomem *base;
101 void __iomem *bar[6];
102 struct completion irq_raised;
103 int last_irq;
Kishon Vijay Abraham Ib7636e82017-10-11 14:14:38 +0530104 int num_irqs;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530105 /* mutex to protect the ioctls */
106 struct mutex mutex;
107 struct miscdevice miscdev;
Kishon Vijay Abraham I834b90512017-08-18 20:28:05 +0530108 enum pci_barno test_reg_bar;
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530109 size_t alignment;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530110};
111
Kishon Vijay Abraham I834b90512017-08-18 20:28:05 +0530112struct pci_endpoint_test_data {
113 enum pci_barno test_reg_bar;
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530114 size_t alignment;
Gustavo Pimentel9133e392018-07-19 10:32:18 +0200115 int irq_type;
Kishon Vijay Abraham I834b90512017-08-18 20:28:05 +0530116};
117
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530118static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test,
119 u32 offset)
120{
121 return readl(test->base + offset);
122}
123
124static inline void pci_endpoint_test_writel(struct pci_endpoint_test *test,
125 u32 offset, u32 value)
126{
127 writel(value, test->base + offset);
128}
129
130static inline u32 pci_endpoint_test_bar_readl(struct pci_endpoint_test *test,
131 int bar, int offset)
132{
133 return readl(test->bar[bar] + offset);
134}
135
136static inline void pci_endpoint_test_bar_writel(struct pci_endpoint_test *test,
137 int bar, u32 offset, u32 value)
138{
139 writel(value, test->bar[bar] + offset);
140}
141
142static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id)
143{
144 struct pci_endpoint_test *test = dev_id;
145 u32 reg;
146
147 reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
148 if (reg & STATUS_IRQ_RAISED) {
149 test->last_irq = irq;
150 complete(&test->irq_raised);
151 reg &= ~STATUS_IRQ_RAISED;
152 }
153 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS,
154 reg);
155
156 return IRQ_HANDLED;
157}
158
159static bool pci_endpoint_test_bar(struct pci_endpoint_test *test,
160 enum pci_barno barno)
161{
162 int j;
163 u32 val;
164 int size;
Kishon Vijay Abraham Icda370e2017-08-18 20:28:08 +0530165 struct pci_dev *pdev = test->pdev;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530166
167 if (!test->bar[barno])
168 return false;
169
Kishon Vijay Abraham Icda370e2017-08-18 20:28:08 +0530170 size = pci_resource_len(pdev, barno);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530171
Kishon Vijay Abraham I834b90512017-08-18 20:28:05 +0530172 if (barno == test->test_reg_bar)
173 size = 0x4;
174
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530175 for (j = 0; j < size; j += 4)
176 pci_endpoint_test_bar_writel(test, barno, j, 0xA0A0A0A0);
177
178 for (j = 0; j < size; j += 4) {
179 val = pci_endpoint_test_bar_readl(test, barno, j);
180 if (val != 0xA0A0A0A0)
181 return false;
182 }
183
184 return true;
185}
186
187static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test)
188{
189 u32 val;
190
Gustavo Pimentele8817de2018-07-19 10:32:17 +0200191 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
192 IRQ_TYPE_LEGACY);
193 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 0);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530194 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
195 COMMAND_RAISE_LEGACY_IRQ);
196 val = wait_for_completion_timeout(&test->irq_raised,
197 msecs_to_jiffies(1000));
198 if (!val)
199 return false;
200
201 return true;
202}
203
204static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
205 u8 msi_num)
206{
207 u32 val;
208 struct pci_dev *pdev = test->pdev;
209
Gustavo Pimentele8817de2018-07-19 10:32:17 +0200210 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
211 IRQ_TYPE_MSI);
212 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, msi_num);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530213 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530214 COMMAND_RAISE_MSI_IRQ);
215 val = wait_for_completion_timeout(&test->irq_raised,
216 msecs_to_jiffies(1000));
217 if (!val)
218 return false;
219
Gustavo Pimentelecc57ef2018-05-14 18:27:48 +0100220 if (pci_irq_vector(pdev, msi_num - 1) == test->last_irq)
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530221 return true;
222
223 return false;
224}
225
226static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
227{
228 bool ret = false;
229 void *src_addr;
230 void *dst_addr;
231 dma_addr_t src_phys_addr;
232 dma_addr_t dst_phys_addr;
233 struct pci_dev *pdev = test->pdev;
234 struct device *dev = &pdev->dev;
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530235 void *orig_src_addr;
236 dma_addr_t orig_src_phys_addr;
237 void *orig_dst_addr;
238 dma_addr_t orig_dst_phys_addr;
239 size_t offset;
240 size_t alignment = test->alignment;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530241 u32 src_crc32;
242 u32 dst_crc32;
243
Dan Carpenter343dc692017-09-30 11:15:52 +0300244 if (size > SIZE_MAX - alignment)
245 goto err;
246
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530247 orig_src_addr = dma_alloc_coherent(dev, size + alignment,
248 &orig_src_phys_addr, GFP_KERNEL);
249 if (!orig_src_addr) {
Gustavo Pimentel0e52ea62018-05-14 17:56:23 +0100250 dev_err(dev, "Failed to allocate source buffer\n");
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530251 ret = false;
252 goto err;
253 }
254
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530255 if (alignment && !IS_ALIGNED(orig_src_phys_addr, alignment)) {
256 src_phys_addr = PTR_ALIGN(orig_src_phys_addr, alignment);
257 offset = src_phys_addr - orig_src_phys_addr;
258 src_addr = orig_src_addr + offset;
259 } else {
260 src_phys_addr = orig_src_phys_addr;
261 src_addr = orig_src_addr;
262 }
263
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530264 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_SRC_ADDR,
265 lower_32_bits(src_phys_addr));
266
267 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR,
268 upper_32_bits(src_phys_addr));
269
270 get_random_bytes(src_addr, size);
271 src_crc32 = crc32_le(~0, src_addr, size);
272
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530273 orig_dst_addr = dma_alloc_coherent(dev, size + alignment,
274 &orig_dst_phys_addr, GFP_KERNEL);
275 if (!orig_dst_addr) {
Gustavo Pimentel0e52ea62018-05-14 17:56:23 +0100276 dev_err(dev, "Failed to allocate destination address\n");
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530277 ret = false;
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530278 goto err_orig_src_addr;
279 }
280
281 if (alignment && !IS_ALIGNED(orig_dst_phys_addr, alignment)) {
282 dst_phys_addr = PTR_ALIGN(orig_dst_phys_addr, alignment);
283 offset = dst_phys_addr - orig_dst_phys_addr;
284 dst_addr = orig_dst_addr + offset;
285 } else {
286 dst_phys_addr = orig_dst_phys_addr;
287 dst_addr = orig_dst_addr;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530288 }
289
290 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_DST_ADDR,
291 lower_32_bits(dst_phys_addr));
292 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_DST_ADDR,
293 upper_32_bits(dst_phys_addr));
294
295 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE,
296 size);
297
Gustavo Pimentel9133e392018-07-19 10:32:18 +0200298 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
Gustavo Pimentele8817de2018-07-19 10:32:17 +0200299 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530300 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
Gustavo Pimentele8817de2018-07-19 10:32:17 +0200301 COMMAND_COPY);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530302
303 wait_for_completion(&test->irq_raised);
304
305 dst_crc32 = crc32_le(~0, dst_addr, size);
306 if (dst_crc32 == src_crc32)
307 ret = true;
308
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530309 dma_free_coherent(dev, size + alignment, orig_dst_addr,
310 orig_dst_phys_addr);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530311
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530312err_orig_src_addr:
313 dma_free_coherent(dev, size + alignment, orig_src_addr,
314 orig_src_phys_addr);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530315
316err:
317 return ret;
318}
319
320static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
321{
322 bool ret = false;
323 u32 reg;
324 void *addr;
325 dma_addr_t phys_addr;
326 struct pci_dev *pdev = test->pdev;
327 struct device *dev = &pdev->dev;
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530328 void *orig_addr;
329 dma_addr_t orig_phys_addr;
330 size_t offset;
331 size_t alignment = test->alignment;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530332 u32 crc32;
333
Dan Carpenter343dc692017-09-30 11:15:52 +0300334 if (size > SIZE_MAX - alignment)
335 goto err;
336
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530337 orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr,
338 GFP_KERNEL);
339 if (!orig_addr) {
Gustavo Pimentel0e52ea62018-05-14 17:56:23 +0100340 dev_err(dev, "Failed to allocate address\n");
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530341 ret = false;
342 goto err;
343 }
344
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530345 if (alignment && !IS_ALIGNED(orig_phys_addr, alignment)) {
346 phys_addr = PTR_ALIGN(orig_phys_addr, alignment);
347 offset = phys_addr - orig_phys_addr;
348 addr = orig_addr + offset;
349 } else {
350 phys_addr = orig_phys_addr;
351 addr = orig_addr;
352 }
353
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530354 get_random_bytes(addr, size);
355
356 crc32 = crc32_le(~0, addr, size);
357 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_CHECKSUM,
358 crc32);
359
360 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_SRC_ADDR,
361 lower_32_bits(phys_addr));
362 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR,
363 upper_32_bits(phys_addr));
364
365 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
366
Gustavo Pimentel9133e392018-07-19 10:32:18 +0200367 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
Gustavo Pimentele8817de2018-07-19 10:32:17 +0200368 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530369 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
Gustavo Pimentele8817de2018-07-19 10:32:17 +0200370 COMMAND_READ);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530371
372 wait_for_completion(&test->irq_raised);
373
374 reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
375 if (reg & STATUS_READ_SUCCESS)
376 ret = true;
377
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530378 dma_free_coherent(dev, size + alignment, orig_addr, orig_phys_addr);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530379
380err:
381 return ret;
382}
383
384static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
385{
386 bool ret = false;
387 void *addr;
388 dma_addr_t phys_addr;
389 struct pci_dev *pdev = test->pdev;
390 struct device *dev = &pdev->dev;
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530391 void *orig_addr;
392 dma_addr_t orig_phys_addr;
393 size_t offset;
394 size_t alignment = test->alignment;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530395 u32 crc32;
396
Dan Carpenter343dc692017-09-30 11:15:52 +0300397 if (size > SIZE_MAX - alignment)
398 goto err;
399
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530400 orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr,
401 GFP_KERNEL);
402 if (!orig_addr) {
Gustavo Pimentel0e52ea62018-05-14 17:56:23 +0100403 dev_err(dev, "Failed to allocate destination address\n");
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530404 ret = false;
405 goto err;
406 }
407
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530408 if (alignment && !IS_ALIGNED(orig_phys_addr, alignment)) {
409 phys_addr = PTR_ALIGN(orig_phys_addr, alignment);
410 offset = phys_addr - orig_phys_addr;
411 addr = orig_addr + offset;
412 } else {
413 phys_addr = orig_phys_addr;
414 addr = orig_addr;
415 }
416
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530417 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_DST_ADDR,
418 lower_32_bits(phys_addr));
419 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_DST_ADDR,
420 upper_32_bits(phys_addr));
421
422 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
423
Gustavo Pimentel9133e392018-07-19 10:32:18 +0200424 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
Gustavo Pimentele8817de2018-07-19 10:32:17 +0200425 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530426 pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
Gustavo Pimentele8817de2018-07-19 10:32:17 +0200427 COMMAND_WRITE);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530428
429 wait_for_completion(&test->irq_raised);
430
431 crc32 = crc32_le(~0, addr, size);
432 if (crc32 == pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM))
433 ret = true;
434
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530435 dma_free_coherent(dev, size + alignment, orig_addr, orig_phys_addr);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530436err:
437 return ret;
438}
439
440static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
441 unsigned long arg)
442{
443 int ret = -EINVAL;
444 enum pci_barno bar;
445 struct pci_endpoint_test *test = to_endpoint_test(file->private_data);
446
447 mutex_lock(&test->mutex);
448 switch (cmd) {
449 case PCITEST_BAR:
450 bar = arg;
451 if (bar < 0 || bar > 5)
452 goto ret;
453 ret = pci_endpoint_test_bar(test, bar);
454 break;
455 case PCITEST_LEGACY_IRQ:
456 ret = pci_endpoint_test_legacy_irq(test);
457 break;
458 case PCITEST_MSI:
459 ret = pci_endpoint_test_msi_irq(test, arg);
460 break;
461 case PCITEST_WRITE:
462 ret = pci_endpoint_test_write(test, arg);
463 break;
464 case PCITEST_READ:
465 ret = pci_endpoint_test_read(test, arg);
466 break;
467 case PCITEST_COPY:
468 ret = pci_endpoint_test_copy(test, arg);
469 break;
470 }
471
472ret:
473 mutex_unlock(&test->mutex);
474 return ret;
475}
476
477static const struct file_operations pci_endpoint_test_fops = {
478 .owner = THIS_MODULE,
479 .unlocked_ioctl = pci_endpoint_test_ioctl,
480};
481
482static int pci_endpoint_test_probe(struct pci_dev *pdev,
483 const struct pci_device_id *ent)
484{
485 int i;
486 int err;
Kishon Vijay Abraham I0b915162017-08-18 20:28:07 +0530487 int irq = 0;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530488 int id;
489 char name[20];
490 enum pci_barno bar;
491 void __iomem *base;
492 struct device *dev = &pdev->dev;
493 struct pci_endpoint_test *test;
Kishon Vijay Abraham I834b90512017-08-18 20:28:05 +0530494 struct pci_endpoint_test_data *data;
495 enum pci_barno test_reg_bar = BAR_0;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530496 struct miscdevice *misc_device;
497
498 if (pci_is_bridge(pdev))
499 return -ENODEV;
500
501 test = devm_kzalloc(dev, sizeof(*test), GFP_KERNEL);
502 if (!test)
503 return -ENOMEM;
504
Kishon Vijay Abraham I834b90512017-08-18 20:28:05 +0530505 test->test_reg_bar = 0;
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530506 test->alignment = 0;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530507 test->pdev = pdev;
Kishon Vijay Abraham I834b90512017-08-18 20:28:05 +0530508
Gustavo Pimentel9133e392018-07-19 10:32:18 +0200509 if (no_msi)
510 irq_type = IRQ_TYPE_LEGACY;
511
Kishon Vijay Abraham I834b90512017-08-18 20:28:05 +0530512 data = (struct pci_endpoint_test_data *)ent->driver_data;
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530513 if (data) {
Kishon Vijay Abraham I834b90512017-08-18 20:28:05 +0530514 test_reg_bar = data->test_reg_bar;
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530515 test->alignment = data->alignment;
Gustavo Pimentel9133e392018-07-19 10:32:18 +0200516 irq_type = data->irq_type;
Kishon Vijay Abraham I13107c62017-08-18 20:28:06 +0530517 }
Kishon Vijay Abraham I834b90512017-08-18 20:28:05 +0530518
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530519 init_completion(&test->irq_raised);
520 mutex_init(&test->mutex);
521
522 err = pci_enable_device(pdev);
523 if (err) {
524 dev_err(dev, "Cannot enable PCI device\n");
525 return err;
526 }
527
528 err = pci_request_regions(pdev, DRV_MODULE_NAME);
529 if (err) {
530 dev_err(dev, "Cannot obtain PCI resources\n");
531 goto err_disable_pdev;
532 }
533
534 pci_set_master(pdev);
535
Gustavo Pimentel9133e392018-07-19 10:32:18 +0200536 switch (irq_type) {
537 case IRQ_TYPE_LEGACY:
538 break;
539 case IRQ_TYPE_MSI:
Kishon Vijay Abraham I0b915162017-08-18 20:28:07 +0530540 irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
541 if (irq < 0)
Gustavo Pimentel0e52ea62018-05-14 17:56:23 +0100542 dev_err(dev, "Failed to get MSI interrupts\n");
Kishon Vijay Abraham Ib7636e82017-10-11 14:14:38 +0530543 test->num_irqs = irq;
Gustavo Pimentel9133e392018-07-19 10:32:18 +0200544 break;
545 default:
546 dev_err(dev, "Invalid IRQ type selected\n");
Kishon Vijay Abraham I0b915162017-08-18 20:28:07 +0530547 }
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530548
549 err = devm_request_irq(dev, pdev->irq, pci_endpoint_test_irqhandler,
550 IRQF_SHARED, DRV_MODULE_NAME, test);
551 if (err) {
Gustavo Pimentel0e52ea62018-05-14 17:56:23 +0100552 dev_err(dev, "Failed to request IRQ %d\n", pdev->irq);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530553 goto err_disable_msi;
554 }
555
556 for (i = 1; i < irq; i++) {
Gustavo Pimentelecc57ef2018-05-14 18:27:48 +0100557 err = devm_request_irq(dev, pci_irq_vector(pdev, i),
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530558 pci_endpoint_test_irqhandler,
559 IRQF_SHARED, DRV_MODULE_NAME, test);
560 if (err)
561 dev_err(dev, "failed to request IRQ %d for MSI %d\n",
Gustavo Pimentelecc57ef2018-05-14 18:27:48 +0100562 pci_irq_vector(pdev, i), i + 1);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530563 }
564
565 for (bar = BAR_0; bar <= BAR_5; bar++) {
Niklas Cassel16b17ca2018-03-28 13:50:17 +0200566 if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
567 base = pci_ioremap_bar(pdev, bar);
568 if (!base) {
Gustavo Pimentel0e52ea62018-05-14 17:56:23 +0100569 dev_err(dev, "Failed to read BAR%d\n", bar);
Niklas Cassel16b17ca2018-03-28 13:50:17 +0200570 WARN_ON(bar == test_reg_bar);
571 }
572 test->bar[bar] = base;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530573 }
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530574 }
575
Kishon Vijay Abraham I834b90512017-08-18 20:28:05 +0530576 test->base = test->bar[test_reg_bar];
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530577 if (!test->base) {
Kishon Vijay Abraham I80068c92017-10-11 14:14:36 +0530578 err = -ENOMEM;
Kishon Vijay Abraham I834b90512017-08-18 20:28:05 +0530579 dev_err(dev, "Cannot perform PCI test without BAR%d\n",
580 test_reg_bar);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530581 goto err_iounmap;
582 }
583
584 pci_set_drvdata(pdev, test);
585
586 id = ida_simple_get(&pci_endpoint_test_ida, 0, 0, GFP_KERNEL);
587 if (id < 0) {
Kishon Vijay Abraham I80068c92017-10-11 14:14:36 +0530588 err = id;
Gustavo Pimentel0e52ea62018-05-14 17:56:23 +0100589 dev_err(dev, "Unable to get id\n");
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530590 goto err_iounmap;
591 }
592
593 snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id);
594 misc_device = &test->miscdev;
595 misc_device->minor = MISC_DYNAMIC_MINOR;
Kishon Vijay Abraham I139838f2017-10-11 14:14:37 +0530596 misc_device->name = kstrdup(name, GFP_KERNEL);
597 if (!misc_device->name) {
598 err = -ENOMEM;
599 goto err_ida_remove;
600 }
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530601 misc_device->fops = &pci_endpoint_test_fops,
602
603 err = misc_register(misc_device);
604 if (err) {
Gustavo Pimentel0e52ea62018-05-14 17:56:23 +0100605 dev_err(dev, "Failed to register device\n");
Kishon Vijay Abraham I139838f2017-10-11 14:14:37 +0530606 goto err_kfree_name;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530607 }
608
609 return 0;
610
Kishon Vijay Abraham I139838f2017-10-11 14:14:37 +0530611err_kfree_name:
612 kfree(misc_device->name);
613
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530614err_ida_remove:
615 ida_simple_remove(&pci_endpoint_test_ida, id);
616
617err_iounmap:
618 for (bar = BAR_0; bar <= BAR_5; bar++) {
619 if (test->bar[bar])
620 pci_iounmap(pdev, test->bar[bar]);
621 }
622
Kishon Vijay Abraham Ib7636e82017-10-11 14:14:38 +0530623 for (i = 0; i < irq; i++)
Gustavo Pimentelecc57ef2018-05-14 18:27:48 +0100624 devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), test);
Kishon Vijay Abraham Ib7636e82017-10-11 14:14:38 +0530625
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530626err_disable_msi:
627 pci_disable_msi(pdev);
628 pci_release_regions(pdev);
629
630err_disable_pdev:
631 pci_disable_device(pdev);
632
633 return err;
634}
635
636static void pci_endpoint_test_remove(struct pci_dev *pdev)
637{
638 int id;
Kishon Vijay Abraham Ib7636e82017-10-11 14:14:38 +0530639 int i;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530640 enum pci_barno bar;
641 struct pci_endpoint_test *test = pci_get_drvdata(pdev);
642 struct miscdevice *misc_device = &test->miscdev;
643
644 if (sscanf(misc_device->name, DRV_MODULE_NAME ".%d", &id) != 1)
645 return;
Dan Carpentera2db2662017-09-30 11:16:51 +0300646 if (id < 0)
647 return;
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530648
649 misc_deregister(&test->miscdev);
Kishon Vijay Abraham I139838f2017-10-11 14:14:37 +0530650 kfree(misc_device->name);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530651 ida_simple_remove(&pci_endpoint_test_ida, id);
652 for (bar = BAR_0; bar <= BAR_5; bar++) {
653 if (test->bar[bar])
654 pci_iounmap(pdev, test->bar[bar]);
655 }
Kishon Vijay Abraham Ib7636e82017-10-11 14:14:38 +0530656 for (i = 0; i < test->num_irqs; i++)
Gustavo Pimentelecc57ef2018-05-14 18:27:48 +0100657 devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), test);
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530658 pci_disable_msi(pdev);
659 pci_release_regions(pdev);
660 pci_disable_device(pdev);
661}
662
663static const struct pci_device_id pci_endpoint_test_tbl[] = {
664 { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x) },
665 { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x) },
Gustavo Pimentel14b06dd2018-05-15 15:41:44 +0100666 { PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, 0xedda) },
Kishon Vijay Abraham I2c156ac2017-03-27 15:15:14 +0530667 { }
668};
669MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl);
670
671static struct pci_driver pci_endpoint_test_driver = {
672 .name = DRV_MODULE_NAME,
673 .id_table = pci_endpoint_test_tbl,
674 .probe = pci_endpoint_test_probe,
675 .remove = pci_endpoint_test_remove,
676};
677module_pci_driver(pci_endpoint_test_driver);
678
679MODULE_DESCRIPTION("PCI ENDPOINT TEST HOST DRIVER");
680MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
681MODULE_LICENSE("GPL v2");