Commit | Line | Data |
---|---|---|
349e7a85 KVA |
1 | /** |
2 | * 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/io.h> | |
23 | #include <linux/module.h> | |
24 | #include <linux/slab.h> | |
25 | #include <linux/pci_ids.h> | |
26 | #include <linux/random.h> | |
27 | ||
28 | #include <linux/pci-epc.h> | |
29 | #include <linux/pci-epf.h> | |
30 | #include <linux/pci_regs.h> | |
31 | ||
32 | #define COMMAND_RAISE_LEGACY_IRQ BIT(0) | |
33 | #define COMMAND_RAISE_MSI_IRQ BIT(1) | |
34 | #define MSI_NUMBER_SHIFT 2 | |
35 | #define MSI_NUMBER_MASK (0x3f << MSI_NUMBER_SHIFT) | |
36 | #define COMMAND_READ BIT(8) | |
37 | #define COMMAND_WRITE BIT(9) | |
38 | #define COMMAND_COPY BIT(10) | |
39 | ||
40 | #define STATUS_READ_SUCCESS BIT(0) | |
41 | #define STATUS_READ_FAIL BIT(1) | |
42 | #define STATUS_WRITE_SUCCESS BIT(2) | |
43 | #define STATUS_WRITE_FAIL BIT(3) | |
44 | #define STATUS_COPY_SUCCESS BIT(4) | |
45 | #define STATUS_COPY_FAIL BIT(5) | |
46 | #define STATUS_IRQ_RAISED BIT(6) | |
47 | #define STATUS_SRC_ADDR_INVALID BIT(7) | |
48 | #define STATUS_DST_ADDR_INVALID BIT(8) | |
49 | ||
50 | #define TIMER_RESOLUTION 1 | |
51 | ||
52 | static struct workqueue_struct *kpcitest_workqueue; | |
53 | ||
54 | struct pci_epf_test { | |
55 | void *reg[6]; | |
56 | struct pci_epf *epf; | |
57 | struct delayed_work cmd_handler; | |
58 | }; | |
59 | ||
60 | struct pci_epf_test_reg { | |
61 | u32 magic; | |
62 | u32 command; | |
63 | u32 status; | |
64 | u64 src_addr; | |
65 | u64 dst_addr; | |
66 | u32 size; | |
67 | u32 checksum; | |
68 | } __packed; | |
69 | ||
70 | static struct pci_epf_header test_header = { | |
71 | .vendorid = PCI_ANY_ID, | |
72 | .deviceid = PCI_ANY_ID, | |
73 | .baseclass_code = PCI_CLASS_OTHERS, | |
74 | .interrupt_pin = PCI_INTERRUPT_INTA, | |
75 | }; | |
76 | ||
77 | static int bar_size[] = { 512, 1024, 16384, 131072, 1048576 }; | |
78 | ||
79 | static int pci_epf_test_copy(struct pci_epf_test *epf_test) | |
80 | { | |
81 | int ret; | |
82 | void __iomem *src_addr; | |
83 | void __iomem *dst_addr; | |
84 | phys_addr_t src_phys_addr; | |
85 | phys_addr_t dst_phys_addr; | |
86 | struct pci_epf *epf = epf_test->epf; | |
87 | struct device *dev = &epf->dev; | |
88 | struct pci_epc *epc = epf->epc; | |
89 | struct pci_epf_test_reg *reg = epf_test->reg[0]; | |
90 | ||
91 | src_addr = pci_epc_mem_alloc_addr(epc, &src_phys_addr, reg->size); | |
92 | if (!src_addr) { | |
93 | dev_err(dev, "failed to allocate source address\n"); | |
94 | reg->status = STATUS_SRC_ADDR_INVALID; | |
95 | ret = -ENOMEM; | |
96 | goto err; | |
97 | } | |
98 | ||
99 | ret = pci_epc_map_addr(epc, src_phys_addr, reg->src_addr, reg->size); | |
100 | if (ret) { | |
101 | dev_err(dev, "failed to map source address\n"); | |
102 | reg->status = STATUS_SRC_ADDR_INVALID; | |
103 | goto err_src_addr; | |
104 | } | |
105 | ||
106 | dst_addr = pci_epc_mem_alloc_addr(epc, &dst_phys_addr, reg->size); | |
107 | if (!dst_addr) { | |
108 | dev_err(dev, "failed to allocate destination address\n"); | |
109 | reg->status = STATUS_DST_ADDR_INVALID; | |
110 | ret = -ENOMEM; | |
111 | goto err_src_map_addr; | |
112 | } | |
113 | ||
114 | ret = pci_epc_map_addr(epc, dst_phys_addr, reg->dst_addr, reg->size); | |
115 | if (ret) { | |
116 | dev_err(dev, "failed to map destination address\n"); | |
117 | reg->status = STATUS_DST_ADDR_INVALID; | |
118 | goto err_dst_addr; | |
119 | } | |
120 | ||
121 | memcpy(dst_addr, src_addr, reg->size); | |
122 | ||
123 | pci_epc_unmap_addr(epc, dst_phys_addr); | |
124 | ||
125 | err_dst_addr: | |
126 | pci_epc_mem_free_addr(epc, dst_phys_addr, dst_addr, reg->size); | |
127 | ||
128 | err_src_map_addr: | |
129 | pci_epc_unmap_addr(epc, src_phys_addr); | |
130 | ||
131 | err_src_addr: | |
132 | pci_epc_mem_free_addr(epc, src_phys_addr, src_addr, reg->size); | |
133 | ||
134 | err: | |
135 | return ret; | |
136 | } | |
137 | ||
138 | static int pci_epf_test_read(struct pci_epf_test *epf_test) | |
139 | { | |
140 | int ret; | |
141 | void __iomem *src_addr; | |
142 | void *buf; | |
143 | u32 crc32; | |
144 | phys_addr_t phys_addr; | |
145 | struct pci_epf *epf = epf_test->epf; | |
146 | struct device *dev = &epf->dev; | |
147 | struct pci_epc *epc = epf->epc; | |
148 | struct pci_epf_test_reg *reg = epf_test->reg[0]; | |
149 | ||
150 | src_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size); | |
151 | if (!src_addr) { | |
152 | dev_err(dev, "failed to allocate address\n"); | |
153 | reg->status = STATUS_SRC_ADDR_INVALID; | |
154 | ret = -ENOMEM; | |
155 | goto err; | |
156 | } | |
157 | ||
158 | ret = pci_epc_map_addr(epc, phys_addr, reg->src_addr, reg->size); | |
159 | if (ret) { | |
160 | dev_err(dev, "failed to map address\n"); | |
161 | reg->status = STATUS_SRC_ADDR_INVALID; | |
162 | goto err_addr; | |
163 | } | |
164 | ||
165 | buf = kzalloc(reg->size, GFP_KERNEL); | |
166 | if (!buf) { | |
167 | ret = -ENOMEM; | |
168 | goto err_map_addr; | |
169 | } | |
170 | ||
171 | memcpy(buf, src_addr, reg->size); | |
172 | ||
173 | crc32 = crc32_le(~0, buf, reg->size); | |
174 | if (crc32 != reg->checksum) | |
175 | ret = -EIO; | |
176 | ||
177 | kfree(buf); | |
178 | ||
179 | err_map_addr: | |
180 | pci_epc_unmap_addr(epc, phys_addr); | |
181 | ||
182 | err_addr: | |
183 | pci_epc_mem_free_addr(epc, phys_addr, src_addr, reg->size); | |
184 | ||
185 | err: | |
186 | return ret; | |
187 | } | |
188 | ||
189 | static int pci_epf_test_write(struct pci_epf_test *epf_test) | |
190 | { | |
191 | int ret; | |
192 | void __iomem *dst_addr; | |
193 | void *buf; | |
194 | phys_addr_t phys_addr; | |
195 | struct pci_epf *epf = epf_test->epf; | |
196 | struct device *dev = &epf->dev; | |
197 | struct pci_epc *epc = epf->epc; | |
198 | struct pci_epf_test_reg *reg = epf_test->reg[0]; | |
199 | ||
200 | dst_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size); | |
201 | if (!dst_addr) { | |
202 | dev_err(dev, "failed to allocate address\n"); | |
203 | reg->status = STATUS_DST_ADDR_INVALID; | |
204 | ret = -ENOMEM; | |
205 | goto err; | |
206 | } | |
207 | ||
208 | ret = pci_epc_map_addr(epc, phys_addr, reg->dst_addr, reg->size); | |
209 | if (ret) { | |
210 | dev_err(dev, "failed to map address\n"); | |
211 | reg->status = STATUS_DST_ADDR_INVALID; | |
212 | goto err_addr; | |
213 | } | |
214 | ||
215 | buf = kzalloc(reg->size, GFP_KERNEL); | |
216 | if (!buf) { | |
217 | ret = -ENOMEM; | |
218 | goto err_map_addr; | |
219 | } | |
220 | ||
221 | get_random_bytes(buf, reg->size); | |
222 | reg->checksum = crc32_le(~0, buf, reg->size); | |
223 | ||
224 | memcpy(dst_addr, buf, reg->size); | |
225 | ||
226 | /* | |
227 | * wait 1ms inorder for the write to complete. Without this delay L3 | |
228 | * error in observed in the host system. | |
229 | */ | |
230 | mdelay(1); | |
231 | ||
232 | kfree(buf); | |
233 | ||
234 | err_map_addr: | |
235 | pci_epc_unmap_addr(epc, phys_addr); | |
236 | ||
237 | err_addr: | |
238 | pci_epc_mem_free_addr(epc, phys_addr, dst_addr, reg->size); | |
239 | ||
240 | err: | |
241 | return ret; | |
242 | } | |
243 | ||
244 | static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test) | |
245 | { | |
246 | u8 irq; | |
247 | u8 msi_count; | |
248 | struct pci_epf *epf = epf_test->epf; | |
249 | struct pci_epc *epc = epf->epc; | |
250 | struct pci_epf_test_reg *reg = epf_test->reg[0]; | |
251 | ||
252 | reg->status |= STATUS_IRQ_RAISED; | |
253 | msi_count = pci_epc_get_msi(epc); | |
254 | irq = (reg->command & MSI_NUMBER_MASK) >> MSI_NUMBER_SHIFT; | |
255 | if (irq > msi_count || msi_count <= 0) | |
256 | pci_epc_raise_irq(epc, PCI_EPC_IRQ_LEGACY, 0); | |
257 | else | |
258 | pci_epc_raise_irq(epc, PCI_EPC_IRQ_MSI, irq); | |
259 | } | |
260 | ||
261 | static void pci_epf_test_cmd_handler(struct work_struct *work) | |
262 | { | |
263 | int ret; | |
264 | u8 irq; | |
265 | u8 msi_count; | |
3ecf3232 | 266 | u32 command; |
349e7a85 KVA |
267 | struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test, |
268 | cmd_handler.work); | |
269 | struct pci_epf *epf = epf_test->epf; | |
270 | struct pci_epc *epc = epf->epc; | |
09232c7a | 271 | volatile struct pci_epf_test_reg *reg = epf_test->reg[0]; |
349e7a85 | 272 | |
3ecf3232 KVA |
273 | command = reg->command; |
274 | if (!command) | |
349e7a85 KVA |
275 | goto reset_handler; |
276 | ||
3ecf3232 KVA |
277 | reg->command = 0; |
278 | ||
279 | if (command & COMMAND_RAISE_LEGACY_IRQ) { | |
349e7a85 KVA |
280 | reg->status = STATUS_IRQ_RAISED; |
281 | pci_epc_raise_irq(epc, PCI_EPC_IRQ_LEGACY, 0); | |
282 | goto reset_handler; | |
283 | } | |
284 | ||
3ecf3232 | 285 | if (command & COMMAND_WRITE) { |
349e7a85 KVA |
286 | ret = pci_epf_test_write(epf_test); |
287 | if (ret) | |
288 | reg->status |= STATUS_WRITE_FAIL; | |
289 | else | |
290 | reg->status |= STATUS_WRITE_SUCCESS; | |
291 | pci_epf_test_raise_irq(epf_test); | |
292 | goto reset_handler; | |
293 | } | |
294 | ||
3ecf3232 | 295 | if (command & COMMAND_READ) { |
349e7a85 KVA |
296 | ret = pci_epf_test_read(epf_test); |
297 | if (!ret) | |
298 | reg->status |= STATUS_READ_SUCCESS; | |
299 | else | |
300 | reg->status |= STATUS_READ_FAIL; | |
301 | pci_epf_test_raise_irq(epf_test); | |
302 | goto reset_handler; | |
303 | } | |
304 | ||
3ecf3232 | 305 | if (command & COMMAND_COPY) { |
349e7a85 KVA |
306 | ret = pci_epf_test_copy(epf_test); |
307 | if (!ret) | |
308 | reg->status |= STATUS_COPY_SUCCESS; | |
309 | else | |
310 | reg->status |= STATUS_COPY_FAIL; | |
311 | pci_epf_test_raise_irq(epf_test); | |
312 | goto reset_handler; | |
313 | } | |
314 | ||
3ecf3232 | 315 | if (command & COMMAND_RAISE_MSI_IRQ) { |
349e7a85 | 316 | msi_count = pci_epc_get_msi(epc); |
3ecf3232 | 317 | irq = (command & MSI_NUMBER_MASK) >> MSI_NUMBER_SHIFT; |
349e7a85 KVA |
318 | if (irq > msi_count || msi_count <= 0) |
319 | goto reset_handler; | |
320 | reg->status = STATUS_IRQ_RAISED; | |
321 | pci_epc_raise_irq(epc, PCI_EPC_IRQ_MSI, irq); | |
322 | goto reset_handler; | |
323 | } | |
324 | ||
325 | reset_handler: | |
349e7a85 KVA |
326 | queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler, |
327 | msecs_to_jiffies(1)); | |
328 | } | |
329 | ||
330 | static void pci_epf_test_linkup(struct pci_epf *epf) | |
331 | { | |
332 | struct pci_epf_test *epf_test = epf_get_drvdata(epf); | |
333 | ||
334 | queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler, | |
335 | msecs_to_jiffies(1)); | |
336 | } | |
337 | ||
338 | static void pci_epf_test_unbind(struct pci_epf *epf) | |
339 | { | |
340 | struct pci_epf_test *epf_test = epf_get_drvdata(epf); | |
341 | struct pci_epc *epc = epf->epc; | |
342 | int bar; | |
343 | ||
344 | cancel_delayed_work(&epf_test->cmd_handler); | |
345 | pci_epc_stop(epc); | |
346 | for (bar = BAR_0; bar <= BAR_5; bar++) { | |
347 | if (epf_test->reg[bar]) { | |
348 | pci_epf_free_space(epf, epf_test->reg[bar], bar); | |
349 | pci_epc_clear_bar(epc, bar); | |
350 | } | |
351 | } | |
352 | } | |
353 | ||
354 | static int pci_epf_test_set_bar(struct pci_epf *epf) | |
355 | { | |
356 | int flags; | |
357 | int bar; | |
358 | int ret; | |
359 | struct pci_epf_bar *epf_bar; | |
360 | struct pci_epc *epc = epf->epc; | |
361 | struct device *dev = &epf->dev; | |
362 | struct pci_epf_test *epf_test = epf_get_drvdata(epf); | |
363 | ||
364 | flags = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32; | |
365 | if (sizeof(dma_addr_t) == 0x8) | |
366 | flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; | |
367 | ||
368 | for (bar = BAR_0; bar <= BAR_5; bar++) { | |
369 | epf_bar = &epf->bar[bar]; | |
370 | ret = pci_epc_set_bar(epc, bar, epf_bar->phys_addr, | |
371 | epf_bar->size, flags); | |
372 | if (ret) { | |
373 | pci_epf_free_space(epf, epf_test->reg[bar], bar); | |
374 | dev_err(dev, "failed to set BAR%d\n", bar); | |
375 | if (bar == BAR_0) | |
376 | return ret; | |
377 | } | |
378 | } | |
379 | ||
380 | return 0; | |
381 | } | |
382 | ||
383 | static int pci_epf_test_alloc_space(struct pci_epf *epf) | |
384 | { | |
385 | struct pci_epf_test *epf_test = epf_get_drvdata(epf); | |
386 | struct device *dev = &epf->dev; | |
387 | void *base; | |
388 | int bar; | |
389 | ||
390 | base = pci_epf_alloc_space(epf, sizeof(struct pci_epf_test_reg), | |
391 | BAR_0); | |
392 | if (!base) { | |
393 | dev_err(dev, "failed to allocated register space\n"); | |
394 | return -ENOMEM; | |
395 | } | |
396 | epf_test->reg[0] = base; | |
397 | ||
398 | for (bar = BAR_1; bar <= BAR_5; bar++) { | |
399 | base = pci_epf_alloc_space(epf, bar_size[bar - 1], bar); | |
400 | if (!base) | |
401 | dev_err(dev, "failed to allocate space for BAR%d\n", | |
402 | bar); | |
403 | epf_test->reg[bar] = base; | |
404 | } | |
405 | ||
406 | return 0; | |
407 | } | |
408 | ||
409 | static int pci_epf_test_bind(struct pci_epf *epf) | |
410 | { | |
411 | int ret; | |
412 | struct pci_epf_header *header = epf->header; | |
413 | struct pci_epc *epc = epf->epc; | |
414 | struct device *dev = &epf->dev; | |
415 | ||
416 | if (WARN_ON_ONCE(!epc)) | |
417 | return -EINVAL; | |
418 | ||
419 | ret = pci_epc_write_header(epc, header); | |
420 | if (ret) { | |
421 | dev_err(dev, "configuration header write failed\n"); | |
422 | return ret; | |
423 | } | |
424 | ||
425 | ret = pci_epf_test_alloc_space(epf); | |
426 | if (ret) | |
427 | return ret; | |
428 | ||
429 | ret = pci_epf_test_set_bar(epf); | |
430 | if (ret) | |
431 | return ret; | |
432 | ||
433 | ret = pci_epc_set_msi(epc, epf->msi_interrupts); | |
434 | if (ret) | |
435 | return ret; | |
436 | ||
437 | return 0; | |
438 | } | |
439 | ||
440 | static int pci_epf_test_probe(struct pci_epf *epf) | |
441 | { | |
442 | struct pci_epf_test *epf_test; | |
443 | struct device *dev = &epf->dev; | |
444 | ||
445 | epf_test = devm_kzalloc(dev, sizeof(*epf_test), GFP_KERNEL); | |
446 | if (!epf_test) | |
447 | return -ENOMEM; | |
448 | ||
449 | epf->header = &test_header; | |
450 | epf_test->epf = epf; | |
451 | ||
452 | INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler); | |
453 | ||
454 | epf_set_drvdata(epf, epf_test); | |
455 | return 0; | |
456 | } | |
457 | ||
458 | static int pci_epf_test_remove(struct pci_epf *epf) | |
459 | { | |
460 | struct pci_epf_test *epf_test = epf_get_drvdata(epf); | |
461 | ||
462 | kfree(epf_test); | |
463 | return 0; | |
464 | } | |
465 | ||
466 | static struct pci_epf_ops ops = { | |
467 | .unbind = pci_epf_test_unbind, | |
468 | .bind = pci_epf_test_bind, | |
469 | .linkup = pci_epf_test_linkup, | |
470 | }; | |
471 | ||
472 | static const struct pci_epf_device_id pci_epf_test_ids[] = { | |
473 | { | |
474 | .name = "pci_epf_test", | |
475 | }, | |
476 | {}, | |
477 | }; | |
478 | ||
479 | static struct pci_epf_driver test_driver = { | |
480 | .driver.name = "pci_epf_test", | |
481 | .probe = pci_epf_test_probe, | |
482 | .remove = pci_epf_test_remove, | |
483 | .id_table = pci_epf_test_ids, | |
484 | .ops = &ops, | |
485 | .owner = THIS_MODULE, | |
486 | }; | |
487 | ||
488 | static int __init pci_epf_test_init(void) | |
489 | { | |
490 | int ret; | |
491 | ||
492 | kpcitest_workqueue = alloc_workqueue("kpcitest", | |
493 | WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); | |
494 | ret = pci_epf_register_driver(&test_driver); | |
495 | if (ret) { | |
496 | pr_err("failed to register pci epf test driver --> %d\n", ret); | |
497 | return ret; | |
498 | } | |
499 | ||
500 | return 0; | |
501 | } | |
502 | module_init(pci_epf_test_init); | |
503 | ||
504 | static void __exit pci_epf_test_exit(void) | |
505 | { | |
506 | pci_epf_unregister_driver(&test_driver); | |
507 | } | |
508 | module_exit(pci_epf_test_exit); | |
509 | ||
510 | MODULE_DESCRIPTION("PCI EPF TEST DRIVER"); | |
511 | MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>"); | |
512 | MODULE_LICENSE("GPL v2"); |