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