aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnatolij Gustschin <agust@denx.de>2017-06-14 11:36:35 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-07-17 11:26:15 -0400
commit34d1dc17ce978ae76e676d401b48fe9d004aa948 (patch)
tree5c1931f0cab2f1f8b0236835a8e2d25ec0f98687
parentb37fa56069ce61d97a77dadca8dd65e522db3387 (diff)
fpga manager: Add Altera CvP driver
Add FPGA manager driver for loading Arria-V/Cyclone-V/Stratix-V and Arria-10 FPGAs via CvP. Signed-off-by: Anatolij Gustschin <agust@denx.de> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Signed-off-by: Alan Tull <atull@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--Documentation/ABI/testing/sysfs-driver-altera-cvp8
-rw-r--r--drivers/fpga/Kconfig7
-rw-r--r--drivers/fpga/Makefile1
-rw-r--r--drivers/fpga/altera-cvp.c500
4 files changed, 516 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-altera-cvp b/Documentation/ABI/testing/sysfs-driver-altera-cvp
new file mode 100644
index 000000000000..8cde64a71edb
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-altera-cvp
@@ -0,0 +1,8 @@
1What: /sys/bus/pci/drivers/altera-cvp/chkcfg
2Date: May 2017
3Kernel Version: 4.13
4Contact: Anatolij Gustschin <agust@denx.de>
5Description:
6 Contains either 1 or 0 and controls if configuration
7 error checking in altera-cvp driver is turned on or
8 off.
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index e0e2d6db669f..ad5448f718b3 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -24,6 +24,13 @@ config FPGA_MGR_ICE40_SPI
24 help 24 help
25 FPGA manager driver support for Lattice iCE40 FPGAs over SPI. 25 FPGA manager driver support for Lattice iCE40 FPGAs over SPI.
26 26
27config FPGA_MGR_ALTERA_CVP
28 tristate "Altera Arria-V/Cyclone-V/Stratix-V CvP FPGA Manager"
29 depends on PCI
30 help
31 FPGA manager driver support for Arria-V, Cyclone-V, Stratix-V
32 and Arria 10 Altera FPGAs using the CvP interface over PCIe.
33
27config FPGA_MGR_ALTERA_PS_SPI 34config FPGA_MGR_ALTERA_PS_SPI
28 tristate "Altera FPGA Passive Serial over SPI" 35 tristate "Altera FPGA Passive Serial over SPI"
29 depends on SPI 36 depends on SPI
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index e75d3570e26a..e09895f0525b 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -6,6 +6,7 @@
6obj-$(CONFIG_FPGA) += fpga-mgr.o 6obj-$(CONFIG_FPGA) += fpga-mgr.o
7 7
8# FPGA Manager Drivers 8# FPGA Manager Drivers
9obj-$(CONFIG_FPGA_MGR_ALTERA_CVP) += altera-cvp.o
9obj-$(CONFIG_FPGA_MGR_ALTERA_PS_SPI) += altera-ps-spi.o 10obj-$(CONFIG_FPGA_MGR_ALTERA_PS_SPI) += altera-ps-spi.o
10obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o 11obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
11obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o 12obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
new file mode 100644
index 000000000000..08629ee69d11
--- /dev/null
+++ b/drivers/fpga/altera-cvp.c
@@ -0,0 +1,500 @@
1/*
2 * FPGA Manager Driver for Altera Arria/Cyclone/Stratix CvP
3 *
4 * Copyright (C) 2017 DENX Software Engineering
5 *
6 * Anatolij Gustschin <agust@denx.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * Manage Altera FPGA firmware using PCIe CvP.
18 * Firmware must be in binary "rbf" format.
19 */
20
21#include <linux/delay.h>
22#include <linux/device.h>
23#include <linux/fpga/fpga-mgr.h>
24#include <linux/module.h>
25#include <linux/pci.h>
26#include <linux/sizes.h>
27
28#define CVP_BAR 0 /* BAR used for data transfer in memory mode */
29#define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */
30#define TIMEOUT_US 2000 /* CVP STATUS timeout for USERMODE polling */
31
32/* Vendor Specific Extended Capability Registers */
33#define VSE_PCIE_EXT_CAP_ID 0x200
34#define VSE_PCIE_EXT_CAP_ID_VAL 0x000b /* 16bit */
35
36#define VSE_CVP_STATUS 0x21c /* 32bit */
37#define VSE_CVP_STATUS_CFG_RDY BIT(18) /* CVP_CONFIG_READY */
38#define VSE_CVP_STATUS_CFG_ERR BIT(19) /* CVP_CONFIG_ERROR */
39#define VSE_CVP_STATUS_CVP_EN BIT(20) /* ctrl block is enabling CVP */
40#define VSE_CVP_STATUS_USERMODE BIT(21) /* USERMODE */
41#define VSE_CVP_STATUS_CFG_DONE BIT(23) /* CVP_CONFIG_DONE */
42#define VSE_CVP_STATUS_PLD_CLK_IN_USE BIT(24) /* PLD_CLK_IN_USE */
43
44#define VSE_CVP_MODE_CTRL 0x220 /* 32bit */
45#define VSE_CVP_MODE_CTRL_CVP_MODE BIT(0) /* CVP (1) or normal mode (0) */
46#define VSE_CVP_MODE_CTRL_HIP_CLK_SEL BIT(1) /* PMA (1) or fabric clock (0) */
47#define VSE_CVP_MODE_CTRL_NUMCLKS_OFF 8 /* NUMCLKS bits offset */
48#define VSE_CVP_MODE_CTRL_NUMCLKS_MASK GENMASK(15, 8)
49
50#define VSE_CVP_DATA 0x228 /* 32bit */
51#define VSE_CVP_PROG_CTRL 0x22c /* 32bit */
52#define VSE_CVP_PROG_CTRL_CONFIG BIT(0)
53#define VSE_CVP_PROG_CTRL_START_XFER BIT(1)
54
55#define VSE_UNCOR_ERR_STATUS 0x234 /* 32bit */
56#define VSE_UNCOR_ERR_CVP_CFG_ERR BIT(5) /* CVP_CONFIG_ERROR_LATCHED */
57
58#define DRV_NAME "altera-cvp"
59#define ALTERA_CVP_MGR_NAME "Altera CvP FPGA Manager"
60
61/* Optional CvP config error status check for debugging */
62static bool altera_cvp_chkcfg;
63
64struct altera_cvp_conf {
65 struct fpga_manager *mgr;
66 struct pci_dev *pci_dev;
67 void __iomem *map;
68 void (*write_data)(struct altera_cvp_conf *, u32);
69 char mgr_name[64];
70 u8 numclks;
71};
72
73static enum fpga_mgr_states altera_cvp_state(struct fpga_manager *mgr)
74{
75 struct altera_cvp_conf *conf = mgr->priv;
76 u32 status;
77
78 pci_read_config_dword(conf->pci_dev, VSE_CVP_STATUS, &status);
79
80 if (status & VSE_CVP_STATUS_CFG_DONE)
81 return FPGA_MGR_STATE_OPERATING;
82
83 if (status & VSE_CVP_STATUS_CVP_EN)
84 return FPGA_MGR_STATE_POWER_UP;
85
86 return FPGA_MGR_STATE_UNKNOWN;
87}
88
89static void altera_cvp_write_data_iomem(struct altera_cvp_conf *conf, u32 val)
90{
91 writel(val, conf->map);
92}
93
94static void altera_cvp_write_data_config(struct altera_cvp_conf *conf, u32 val)
95{
96 pci_write_config_dword(conf->pci_dev, VSE_CVP_DATA, val);
97}
98
99/* switches between CvP clock and internal clock */
100static void altera_cvp_dummy_write(struct altera_cvp_conf *conf)
101{
102 unsigned int i;
103 u32 val;
104
105 /* set 1 CVP clock cycle for every CVP Data Register Write */
106 pci_read_config_dword(conf->pci_dev, VSE_CVP_MODE_CTRL, &val);
107 val &= ~VSE_CVP_MODE_CTRL_NUMCLKS_MASK;
108 val |= 1 << VSE_CVP_MODE_CTRL_NUMCLKS_OFF;
109 pci_write_config_dword(conf->pci_dev, VSE_CVP_MODE_CTRL, val);
110
111 for (i = 0; i < CVP_DUMMY_WR; i++)
112 conf->write_data(conf, 0); /* dummy data, could be any value */
113}
114
115static int altera_cvp_wait_status(struct altera_cvp_conf *conf, u32 status_mask,
116 u32 status_val, int timeout_us)
117{
118 unsigned int retries;
119 u32 val;
120
121 retries = timeout_us / 10;
122 if (timeout_us % 10)
123 retries++;
124
125 do {
126 pci_read_config_dword(conf->pci_dev, VSE_CVP_STATUS, &val);
127 if ((val & status_mask) == status_val)
128 return 0;
129
130 /* use small usleep value to re-check and break early */
131 usleep_range(10, 11);
132 } while (--retries);
133
134 return -ETIMEDOUT;
135}
136
137static int altera_cvp_teardown(struct fpga_manager *mgr,
138 struct fpga_image_info *info)
139{
140 struct altera_cvp_conf *conf = mgr->priv;
141 struct pci_dev *pdev = conf->pci_dev;
142 int ret;
143 u32 val;
144
145 /* STEP 12 - reset START_XFER bit */
146 pci_read_config_dword(pdev, VSE_CVP_PROG_CTRL, &val);
147 val &= ~VSE_CVP_PROG_CTRL_START_XFER;
148 pci_write_config_dword(pdev, VSE_CVP_PROG_CTRL, val);
149
150 /* STEP 13 - reset CVP_CONFIG bit */
151 val &= ~VSE_CVP_PROG_CTRL_CONFIG;
152 pci_write_config_dword(pdev, VSE_CVP_PROG_CTRL, val);
153
154 /*
155 * STEP 14
156 * - set CVP_NUMCLKS to 1 and then issue CVP_DUMMY_WR dummy
157 * writes to the HIP
158 */
159 altera_cvp_dummy_write(conf); /* from CVP clock to internal clock */
160
161 /* STEP 15 - poll CVP_CONFIG_READY bit for 0 with 10us timeout */
162 ret = altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY, 0, 10);
163 if (ret)
164 dev_err(&mgr->dev, "CFG_RDY == 0 timeout\n");
165
166 return ret;
167}
168
169static int altera_cvp_write_init(struct fpga_manager *mgr,
170 struct fpga_image_info *info,
171 const char *buf, size_t count)
172{
173 struct altera_cvp_conf *conf = mgr->priv;
174 struct pci_dev *pdev = conf->pci_dev;
175 u32 iflags, val;
176 int ret;
177
178 iflags = info ? info->flags : 0;
179
180 if (iflags & FPGA_MGR_PARTIAL_RECONFIG) {
181 dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
182 return -EINVAL;
183 }
184
185 /* Determine allowed clock to data ratio */
186 if (iflags & FPGA_MGR_COMPRESSED_BITSTREAM)
187 conf->numclks = 8; /* ratio for all compressed images */
188 else if (iflags & FPGA_MGR_ENCRYPTED_BITSTREAM)
189 conf->numclks = 4; /* for uncompressed and encrypted images */
190 else
191 conf->numclks = 1; /* for uncompressed and unencrypted images */
192
193 /* STEP 1 - read CVP status and check CVP_EN flag */
194 pci_read_config_dword(pdev, VSE_CVP_STATUS, &val);
195 if (!(val & VSE_CVP_STATUS_CVP_EN)) {
196 dev_err(&mgr->dev, "CVP mode off: 0x%04x\n", val);
197 return -ENODEV;
198 }
199
200 if (val & VSE_CVP_STATUS_CFG_RDY) {
201 dev_warn(&mgr->dev, "CvP already started, teardown first\n");
202 ret = altera_cvp_teardown(mgr, info);
203 if (ret)
204 return ret;
205 }
206
207 /*
208 * STEP 2
209 * - set HIP_CLK_SEL and CVP_MODE (must be set in the order mentioned)
210 */
211 /* switch from fabric to PMA clock */
212 pci_read_config_dword(pdev, VSE_CVP_MODE_CTRL, &val);
213 val |= VSE_CVP_MODE_CTRL_HIP_CLK_SEL;
214 pci_write_config_dword(pdev, VSE_CVP_MODE_CTRL, val);
215
216 /* set CVP mode */
217 pci_read_config_dword(pdev, VSE_CVP_MODE_CTRL, &val);
218 val |= VSE_CVP_MODE_CTRL_CVP_MODE;
219 pci_write_config_dword(pdev, VSE_CVP_MODE_CTRL, val);
220
221 /*
222 * STEP 3
223 * - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP
224 */
225 altera_cvp_dummy_write(conf);
226
227 /* STEP 4 - set CVP_CONFIG bit */
228 pci_read_config_dword(pdev, VSE_CVP_PROG_CTRL, &val);
229 /* request control block to begin transfer using CVP */
230 val |= VSE_CVP_PROG_CTRL_CONFIG;
231 pci_write_config_dword(pdev, VSE_CVP_PROG_CTRL, val);
232
233 /* STEP 5 - poll CVP_CONFIG READY for 1 with 10us timeout */
234 ret = altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY,
235 VSE_CVP_STATUS_CFG_RDY, 10);
236 if (ret) {
237 dev_warn(&mgr->dev, "CFG_RDY == 1 timeout\n");
238 return ret;
239 }
240
241 /*
242 * STEP 6
243 * - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP
244 */
245 altera_cvp_dummy_write(conf);
246
247 /* STEP 7 - set START_XFER */
248 pci_read_config_dword(pdev, VSE_CVP_PROG_CTRL, &val);
249 val |= VSE_CVP_PROG_CTRL_START_XFER;
250 pci_write_config_dword(pdev, VSE_CVP_PROG_CTRL, val);
251
252 /* STEP 8 - start transfer (set CVP_NUMCLKS for bitstream) */
253 pci_read_config_dword(pdev, VSE_CVP_MODE_CTRL, &val);
254 val &= ~VSE_CVP_MODE_CTRL_NUMCLKS_MASK;
255 val |= conf->numclks << VSE_CVP_MODE_CTRL_NUMCLKS_OFF;
256 pci_write_config_dword(pdev, VSE_CVP_MODE_CTRL, val);
257
258 return 0;
259}
260
261static inline int altera_cvp_chk_error(struct fpga_manager *mgr, size_t bytes)
262{
263 struct altera_cvp_conf *conf = mgr->priv;
264 u32 val;
265
266 /* STEP 10 (optional) - check CVP_CONFIG_ERROR flag */
267 pci_read_config_dword(conf->pci_dev, VSE_CVP_STATUS, &val);
268 if (val & VSE_CVP_STATUS_CFG_ERR) {
269 dev_err(&mgr->dev, "CVP_CONFIG_ERROR after %zu bytes!\n",
270 bytes);
271 return -EPROTO;
272 }
273 return 0;
274}
275
276static int altera_cvp_write(struct fpga_manager *mgr, const char *buf,
277 size_t count)
278{
279 struct altera_cvp_conf *conf = mgr->priv;
280 const u32 *data;
281 size_t done, remaining;
282 int status = 0;
283 u32 mask;
284
285 /* STEP 9 - write 32-bit data from RBF file to CVP data register */
286 data = (u32 *)buf;
287 remaining = count;
288 done = 0;
289
290 while (remaining >= 4) {
291 conf->write_data(conf, *data++);
292 done += 4;
293 remaining -= 4;
294
295 /*
296 * STEP 10 (optional) and STEP 11
297 * - check error flag
298 * - loop until data transfer completed
299 * Config images can be huge (more than 40 MiB), so
300 * only check after a new 4k data block has been written.
301 * This reduces the number of checks and speeds up the
302 * configuration process.
303 */
304 if (altera_cvp_chkcfg && !(done % SZ_4K)) {
305 status = altera_cvp_chk_error(mgr, done);
306 if (status < 0)
307 return status;
308 }
309 }
310
311 /* write up to 3 trailing bytes, if any */
312 mask = BIT(remaining * 8) - 1;
313 if (mask)
314 conf->write_data(conf, *data & mask);
315
316 if (altera_cvp_chkcfg)
317 status = altera_cvp_chk_error(mgr, count);
318
319 return status;
320}
321
322static int altera_cvp_write_complete(struct fpga_manager *mgr,
323 struct fpga_image_info *info)
324{
325 struct altera_cvp_conf *conf = mgr->priv;
326 struct pci_dev *pdev = conf->pci_dev;
327 int ret;
328 u32 mask;
329 u32 val;
330
331 ret = altera_cvp_teardown(mgr, info);
332 if (ret)
333 return ret;
334
335 /* STEP 16 - check CVP_CONFIG_ERROR_LATCHED bit */
336 pci_read_config_dword(pdev, VSE_UNCOR_ERR_STATUS, &val);
337 if (val & VSE_UNCOR_ERR_CVP_CFG_ERR) {
338 dev_err(&mgr->dev, "detected CVP_CONFIG_ERROR_LATCHED!\n");
339 return -EPROTO;
340 }
341
342 /* STEP 17 - reset CVP_MODE and HIP_CLK_SEL bit */
343 pci_read_config_dword(pdev, VSE_CVP_MODE_CTRL, &val);
344 val &= ~VSE_CVP_MODE_CTRL_HIP_CLK_SEL;
345 val &= ~VSE_CVP_MODE_CTRL_CVP_MODE;
346 pci_write_config_dword(pdev, VSE_CVP_MODE_CTRL, val);
347
348 /* STEP 18 - poll PLD_CLK_IN_USE and USER_MODE bits */
349 mask = VSE_CVP_STATUS_PLD_CLK_IN_USE | VSE_CVP_STATUS_USERMODE;
350 ret = altera_cvp_wait_status(conf, mask, mask, TIMEOUT_US);
351 if (ret)
352 dev_err(&mgr->dev, "PLD_CLK_IN_USE|USERMODE timeout\n");
353
354 return ret;
355}
356
357static const struct fpga_manager_ops altera_cvp_ops = {
358 .state = altera_cvp_state,
359 .write_init = altera_cvp_write_init,
360 .write = altera_cvp_write,
361 .write_complete = altera_cvp_write_complete,
362};
363
364static ssize_t show_chkcfg(struct device_driver *dev, char *buf)
365{
366 return snprintf(buf, 3, "%d\n", altera_cvp_chkcfg);
367}
368
369static ssize_t store_chkcfg(struct device_driver *drv, const char *buf,
370 size_t count)
371{
372 int ret;
373
374 ret = kstrtobool(buf, &altera_cvp_chkcfg);
375 if (ret)
376 return ret;
377
378 return count;
379}
380
381static DRIVER_ATTR(chkcfg, 0600, show_chkcfg, store_chkcfg);
382
383static int altera_cvp_probe(struct pci_dev *pdev,
384 const struct pci_device_id *dev_id);
385static void altera_cvp_remove(struct pci_dev *pdev);
386
387#define PCI_VENDOR_ID_ALTERA 0x1172
388
389static struct pci_device_id altera_cvp_id_tbl[] = {
390 { PCI_VDEVICE(ALTERA, PCI_ANY_ID) },
391 { }
392};
393MODULE_DEVICE_TABLE(pci, altera_cvp_id_tbl);
394
395static struct pci_driver altera_cvp_driver = {
396 .name = DRV_NAME,
397 .id_table = altera_cvp_id_tbl,
398 .probe = altera_cvp_probe,
399 .remove = altera_cvp_remove,
400};
401
402static int altera_cvp_probe(struct pci_dev *pdev,
403 const struct pci_device_id *dev_id)
404{
405 struct altera_cvp_conf *conf;
406 u16 cmd, val;
407 int ret;
408
409 /*
410 * First check if this is the expected FPGA device. PCI config
411 * space access works without enabling the PCI device, memory
412 * space access is enabled further down.
413 */
414 pci_read_config_word(pdev, VSE_PCIE_EXT_CAP_ID, &val);
415 if (val != VSE_PCIE_EXT_CAP_ID_VAL) {
416 dev_err(&pdev->dev, "Wrong EXT_CAP_ID value 0x%x\n", val);
417 return -ENODEV;
418 }
419
420 conf = devm_kzalloc(&pdev->dev, sizeof(*conf), GFP_KERNEL);
421 if (!conf)
422 return -ENOMEM;
423
424 /*
425 * Enable memory BAR access. We cannot use pci_enable_device() here
426 * because it will make the driver unusable with FPGA devices that
427 * have additional big IOMEM resources (e.g. 4GiB BARs) on 32-bit
428 * platform. Such BARs will not have an assigned address range and
429 * pci_enable_device() will fail, complaining about not claimed BAR,
430 * even if the concerned BAR is not needed for FPGA configuration
431 * at all. Thus, enable the device via PCI config space command.
432 */
433 pci_read_config_word(pdev, PCI_COMMAND, &cmd);
434 if (!(cmd & PCI_COMMAND_MEMORY)) {
435 cmd |= PCI_COMMAND_MEMORY;
436 pci_write_config_word(pdev, PCI_COMMAND, cmd);
437 }
438
439 ret = pci_request_region(pdev, CVP_BAR, "CVP");
440 if (ret) {
441 dev_err(&pdev->dev, "Requesting CVP BAR region failed\n");
442 goto err_disable;
443 }
444
445 conf->pci_dev = pdev;
446 conf->write_data = altera_cvp_write_data_iomem;
447
448 conf->map = pci_iomap(pdev, CVP_BAR, 0);
449 if (!conf->map) {
450 dev_warn(&pdev->dev, "Mapping CVP BAR failed\n");
451 conf->write_data = altera_cvp_write_data_config;
452 }
453
454 snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
455 ALTERA_CVP_MGR_NAME, pci_name(pdev));
456
457 ret = fpga_mgr_register(&pdev->dev, conf->mgr_name,
458 &altera_cvp_ops, conf);
459 if (ret)
460 goto err_unmap;
461
462 ret = driver_create_file(&altera_cvp_driver.driver,
463 &driver_attr_chkcfg);
464 if (ret) {
465 dev_err(&pdev->dev, "Can't create sysfs chkcfg file\n");
466 fpga_mgr_unregister(&pdev->dev);
467 goto err_unmap;
468 }
469
470 return 0;
471
472err_unmap:
473 pci_iounmap(pdev, conf->map);
474 pci_release_region(pdev, CVP_BAR);
475err_disable:
476 cmd &= ~PCI_COMMAND_MEMORY;
477 pci_write_config_word(pdev, PCI_COMMAND, cmd);
478 return ret;
479}
480
481static void altera_cvp_remove(struct pci_dev *pdev)
482{
483 struct fpga_manager *mgr = pci_get_drvdata(pdev);
484 struct altera_cvp_conf *conf = mgr->priv;
485 u16 cmd;
486
487 driver_remove_file(&altera_cvp_driver.driver, &driver_attr_chkcfg);
488 fpga_mgr_unregister(&pdev->dev);
489 pci_iounmap(pdev, conf->map);
490 pci_release_region(pdev, CVP_BAR);
491 pci_read_config_word(pdev, PCI_COMMAND, &cmd);
492 cmd &= ~PCI_COMMAND_MEMORY;
493 pci_write_config_word(pdev, PCI_COMMAND, cmd);
494}
495
496module_pci_driver(altera_cvp_driver);
497
498MODULE_LICENSE("GPL v2");
499MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
500MODULE_DESCRIPTION("Module to load Altera FPGA over CvP");