From patchwork Mon Feb 13 19:17:47 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anatolij Gustschin X-Patchwork-Id: 9570709 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 94CB960572 for ; Mon, 13 Feb 2017 19:17:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8479F2521E for ; Mon, 13 Feb 2017 19:17:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 78F9E27F60; Mon, 13 Feb 2017 19:17:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1FE922521E for ; Mon, 13 Feb 2017 19:17:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752876AbdBMTRz (ORCPT ); Mon, 13 Feb 2017 14:17:55 -0500 Received: from mail-out.m-online.net ([212.18.0.9]:52924 "EHLO mail-out.m-online.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752857AbdBMTRz (ORCPT ); Mon, 13 Feb 2017 14:17:55 -0500 Received: from frontend01.mail.m-online.net (unknown [192.168.8.182]) by mail-out.m-online.net (Postfix) with ESMTP id 3vMb0v0nWqz3hjlC; Mon, 13 Feb 2017 20:17:50 +0100 (CET) Received: from localhost (dynscan1.mnet-online.de [192.168.6.68]) by mail.m-online.net (Postfix) with ESMTP id 3vMb0t5mWnzvkL1; Mon, 13 Feb 2017 20:17:50 +0100 (CET) X-Virus-Scanned: amavisd-new at mnet-online.de Received: from mail.mnet-online.de ([192.168.8.182]) by localhost (dynscan1.mail.m-online.net [192.168.6.68]) (amavisd-new, port 10024) with ESMTP id Le7Fquh1er6F; Mon, 13 Feb 2017 20:17:48 +0100 (CET) X-Auth-Info: sXw7FH2SRUpfR8VDaEhZg+HIobA+m+6Im3G10bXO7Lg= Received: from crub.agik.hopto.org (p4FCB5503.dip0.t-ipconnect.de [79.203.85.3]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.mnet-online.de (Postfix) with ESMTPSA; Mon, 13 Feb 2017 20:17:48 +0100 (CET) From: Anatolij Gustschin To: linux-fpga@vger.kernel.org Cc: Alan Tull , Moritz Fischer Subject: [PATCH] fpga manager: Add Stratix V CvP driver Date: Mon, 13 Feb 2017 20:17:47 +0100 Message-Id: <1487013467-27530-1-git-send-email-agust@denx.de> X-Mailer: git-send-email 2.7.4 Sender: linux-fpga-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add FPGA manager driver for loading Stratix V FPGA using CvP. Signed-off-by: Anatolij Gustschin --- drivers/fpga/Kconfig | 7 + drivers/fpga/Makefile | 1 + drivers/fpga/stratix-cvp.c | 529 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 537 insertions(+) create mode 100644 drivers/fpga/stratix-cvp.c diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index ce861a2..7ef5b06 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -33,6 +33,13 @@ config FPGA_MGR_SOCFPGA_A10 help FPGA manager driver support for Altera Arria10 SoCFPGA. +config FPGA_MGR_STRATIX_CVP + tristate "Altera Stratix V CvP" + depends on PCI + help + FPGA manager driver support for Altera Stratix V using the + CvP interface over PCIe + config FPGA_MGR_ZYNQ_FPGA tristate "Xilinx Zynq FPGA" depends on ARCH_ZYNQ || COMPILE_TEST diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index 8df07bc..0444c78 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o # FPGA Manager Drivers obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o +obj-$(CONFIG_FPGA_MGR_STRATIX_CVP) += stratix-cvp.o obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o # FPGA Bridge Drivers diff --git a/drivers/fpga/stratix-cvp.c b/drivers/fpga/stratix-cvp.c new file mode 100644 index 0000000..5ab3499 --- /dev/null +++ b/drivers/fpga/stratix-cvp.c @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2017 DENX Software Engineering + * + * Anatolij Gustschin + * + * Manage Altera Statix V FPGA firmware using PCIe CvP. + * Firmware must be in binary "rbf" format. + */ + +#include +#include +#include +#include +#include + +/* Vendor Specific Extended Capability Offset */ +#define VSEC_OFFSET 0x200 +#define VSEC_PCIE_EXT_CAP_ID_VAL 0x000b + +#define FNC 0 /* PCIe device function ID */ +#define CVP_BAR 0 /* BAR used for data transfer in memory mode */ +#define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */ +#define TIMEOUT_IN_US 10000 + +/* offsets from VSEC_OFFSET */ +#define VSEC_PCIE_EXT_CAP_ID (VSEC_OFFSET + 0x00) /* 16bit */ +#define VSEC_VERSION 0x02 /* 8bit */ +#define VSEC_NEXT_CAP_OFF 0x03 /* 8bit */ +#define VSEC_ID 0x04 /* 16bit */ +#define VSEC_REV 0x06 /* 8bit */ +#define VSEC_LENGTH 0x07 /* 8bit */ +#define VSEC_ALTERA_MARKER 0x08 /* 32bit */ + +#define VSEC_CVP_STATUS (VSEC_OFFSET + 0x1e) /* 16bit */ +#define VSEC_CVP_STATUS_DATA_ENC (1<<0) /* is treated as encrypted */ +#define VSEC_CVP_STATUS_DATA_COMP (1<<1) /* is treated as compressed */ +#define VSEC_CVP_STATUS_CFG_RDY (1<<2) /* CVP_CONFIG_READY */ +#define VSEC_CVP_STATUS_CFG_ERR (1<<3) /* CVP_CONFIG_ERROR */ +#define VSEC_CVP_STATUS_CVP_EN (1<<4) /* ctrl block is enabling CVP */ +#define VSEC_CVP_STATUS_USERMODE (1<<5) /* USERMODE */ +#define VSEC_CVP_STATUS_CFG_DONE (1<<7) /* CVP_CONFIG_DONE */ +#define VSEC_CVP_STATUS_PLD_CLK_IN_USE (1<<8) /* PLD_CLK_IN_USE */ + +#define VSEC_CVP_MODE_CTRL (VSEC_OFFSET + 0x20) /* 32bit */ +#define VSEC_CVP_MODE_CTRL_CVPMODE (1<<0) /* CVP (1) or normal mode (0) */ +#define VSEC_CVP_MODE_CTRL_HIP_CLK_SEL (1<<1) /* PMA (1) or fabric clock (0) */ +#define VSEC_CVP_MODE_CTRL_FULL_CFG (1<<2) /* CVP_FULLCONFIG */ +#define VSEC_CVP_MODE_CTRL_NUMCLKS (0xff<<8) /* CVP_NUMCLKS */ + +#define VSEC_CVP_DATA (VSEC_OFFSET + 0x28) /* 32bit */ +#define VSEC_CVP_PROG_CTRL (VSEC_OFFSET + 0x2c) /* 32bit */ +#define VSEC_CVP_PROG_CTRL_CONFIG (1<<0) +#define VSEC_CVP_PROG_CTRL_START_XFER (1<<1) + +#define VSEC_UNCOR_ERR_STATUS (VSEC_OFFSET + 0x34) /* 32bit */ +#define VSEC_UNCOR_ERR_MASK (VSEC_OFFSET + 0x38) /* 32bit */ +#define VSEC_UNCOR_ERR_CVP_CFG_ERR (1<<5) /* CVP_CONFIG_ERROR_LATCHED */ + +#define DRV_NAME "stratix-cvp" +#define FIRMWARE_STR "stratixv_cvp.rbf" +#define STRATIX_V_CVP_MGR_NAME "Stratix V CvP FPGA Manager" + +static char *firmware = FIRMWARE_STR; +module_param(firmware, charp, 0664); +MODULE_PARM_DESC(firmware, "RBF file under /lib/firmware for loading via CvP."); + +static int chkcfg; /* use value 1 for debugging only */ +module_param(chkcfg, int, 0664); +MODULE_PARM_DESC(chkcfg, "1 - check CvP status, 0 - skip checking (default 0)"); + +struct stratix_cvp_conf { + struct fpga_manager *mgr; + struct pci_dev *pci_dev; + void __iomem *map; + resource_size_t map_base; + resource_size_t map_len; +}; + +static enum fpga_mgr_states stratix_cvp_state(struct fpga_manager *mgr) +{ + return mgr->state; +} + +static int stratix_cvp_write_init(struct fpga_manager *mgr, + struct fpga_image_info *info, + const char *buf, size_t count) +{ + struct stratix_cvp_conf *conf = mgr->priv; + struct pci_bus *bus = conf->pci_dev->bus; + struct pci_dev *pdev = conf->pci_dev; + int delay_us, i; + u32 val32; + u16 val16; + u32 data; + + if (info && info->flags & FPGA_MGR_PARTIAL_RECONFIG) { + dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); + return -EINVAL; + } + + /* + * STEP 1 - read CVP status and check CVP_EN flag + */ + pci_bus_read_config_word(bus, FNC, VSEC_CVP_STATUS, &val16); + if (!(val16 & VSEC_CVP_STATUS_CVP_EN)) { + dev_err(&mgr->dev, "CVP mode off: 0x%04x\n", val16); + return -EINVAL; + } + + /* + * STEP 2 + * - set HIP_CLK_SEL and CVP_MODE (must be set in the order mentioned) + */ + /* switch from fabric to PMA clock */ + pci_read_config_dword(pdev, VSEC_CVP_MODE_CTRL, &val32); + val32 |= VSEC_CVP_MODE_CTRL_HIP_CLK_SEL; + pci_write_config_dword(pdev, VSEC_CVP_MODE_CTRL, val32); + + /* set CVP mode */ + pci_read_config_dword(pdev, VSEC_CVP_MODE_CTRL, &val32); + val32 |= VSEC_CVP_MODE_CTRL_CVPMODE; + pci_write_config_dword(pdev, VSEC_CVP_MODE_CTRL, val32); + + /* + * STEP 3 + * - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP + */ + pci_read_config_dword(pdev, VSEC_CVP_MODE_CTRL, &val32); + val32 &= ~VSEC_CVP_MODE_CTRL_NUMCLKS; + val32 |= 0x01 << 8; /* 1 clock */ + pci_write_config_dword(pdev, VSEC_CVP_MODE_CTRL, val32); + + /* dummy memory write to switch from internal clock to CVP clock */ + val32 = 0xdeadbeef; + if (conf->map) { + u32 *map = conf->map; + + for (i = 0; i < CVP_DUMMY_WR; i++) + *map++ = val32; /* use MemoryWrite */ + } else { + for (i = 0; i < CVP_DUMMY_WR; i++) + pci_write_config_dword(pdev, VSEC_CVP_DATA, val32); + } + + /* + * STEP 4 - set CVP_CONFIG bit + */ + pci_read_config_dword(pdev, VSEC_CVP_PROG_CTRL, &val32); + /* request control block to begin transfer using CVP */ + val32 |= VSEC_CVP_PROG_CTRL_CONFIG; + pci_write_config_dword(pdev, VSEC_CVP_PROG_CTRL, val32); + + /* + * STEP 5 - poll CVP_CONFIG READY + */ + delay_us = 0; + while (1) { + pci_read_config_word(pdev, VSEC_CVP_STATUS, &val16); + if ((val16 & VSEC_CVP_STATUS_CFG_RDY) == + VSEC_CVP_STATUS_CFG_RDY) + break; + + udelay(1); /* wait 1us */ + + if (delay_us++ > TIMEOUT_IN_US) { + dev_warn(&mgr->dev, "CVP_CONFIG_READY == 1 timeout\n"); + return -ETIMEDOUT; + } + } + + /* + * STEP 6 + * - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP + */ + pci_read_config_dword(pdev, VSEC_CVP_MODE_CTRL, &val32); + val32 &= ~VSEC_CVP_MODE_CTRL_NUMCLKS; + val32 |= 0x01 << 8; /* 1 clock */ + pci_write_config_dword(pdev, VSEC_CVP_MODE_CTRL, val32); + + /* dummy memory write to switch from internal clock to CVP clock */ + val32 = 0xdeadbeef; + if (conf->map) { + u32 *map = conf->map; + + for (i = 0; i < CVP_DUMMY_WR; i++) + *map++ = val32; /* use MemoryWrite */ + } else { + for (i = 0; i < CVP_DUMMY_WR; i++) + pci_write_config_dword(pdev, VSEC_CVP_DATA, val32); + } + + /* + * STEP 7 - set START_XFER + */ + pci_read_config_dword(pdev, VSEC_CVP_PROG_CTRL, &val32); + val32 |= VSEC_CVP_PROG_CTRL_START_XFER; + pci_write_config_dword(pdev, VSEC_CVP_PROG_CTRL, val32); + + /* + * STEP 8 (start transfer) + * check required bitstream type - read CVP status and set CVP_NUMCLKS + * accordingly + * Compressed Encrypted CVP_NUMCLKS + * No No 1 + * Yes No 8 + * No Yes 4 + * Yes Yes 8 + */ + pci_read_config_word(pdev, VSEC_CVP_STATUS, &val16); + val16 &= (VSEC_CVP_STATUS_DATA_ENC | VSEC_CVP_STATUS_DATA_COMP); + switch (val16) { + case VSEC_CVP_STATUS_DATA_ENC: + data = 4; + break; + case VSEC_CVP_STATUS_DATA_COMP: + case (VSEC_CVP_STATUS_DATA_ENC | VSEC_CVP_STATUS_DATA_COMP): + data = 8; + break; + default: + data = 1; + break; + } + + /* write number of CVP clock cycles according to bitstream type */ + pci_read_config_dword(pdev, VSEC_CVP_MODE_CTRL, &val32); + val32 &= ~VSEC_CVP_MODE_CTRL_NUMCLKS; + val32 |= data << 8; /* bitstream specific clock count */ + pci_write_config_dword(pdev, VSEC_CVP_MODE_CTRL, val32); + + return 0; +} + +static inline int stratix_cvp_chk_cfg_error(struct fpga_manager *mgr, + size_t bytes) +{ + struct stratix_cvp_conf *conf = mgr->priv; + u16 val16; + + /* + * STEP 10 (optional) - check CVP_CONFIG_ERROR flag + */ + pci_read_config_word(conf->pci_dev, VSEC_CVP_STATUS, &val16); + if (val16 & VSEC_CVP_STATUS_CFG_ERR) { + dev_err(&mgr->dev, " CVP_CONFIG_ERROR after %d bytes!\n", + bytes); + return -EPROTO; + } + return 0; +} + +static int stratix_cvp_write(struct fpga_manager *mgr, const char *buf, + size_t count) +{ + struct stratix_cvp_conf *conf = mgr->priv; + struct pci_dev *pdev = conf->pci_dev; + const u32 *data, *data_end; + size_t bytes = 0; + int status = 0; + + /* + * STEP 9 + * - write 32-bit configuration data from POF file to CVP data register + */ + data = (u32 *)buf; + data_end = (u32 *)((char *)buf + count); + + if (conf->map) { + u32 *map = conf->map; + + while (data < data_end) { + *map = *data++; + bytes += 4; + + /* + * STEP 10 (optional) and STEP 11 + * - check error flag + * - loop until data transfer completed + */ + if (chkcfg) { + status = stratix_cvp_chk_cfg_error(mgr, bytes); + if (status < 0) + break; + } + } + return status; + } + + while (data < data_end) { + pci_write_config_dword(pdev, VSEC_CVP_DATA, *data++); + bytes += 4; + + /* + * STEP 10 (optional) and STEP 11 + * - check error flag + * - loop until data transfer completed + */ + if (chkcfg) { + status = stratix_cvp_chk_cfg_error(mgr, bytes); + if (status < 0) + break; + } + } + + return status; +} + +static int stratix_cvp_write_complete(struct fpga_manager *mgr, + struct fpga_image_info *info) +{ + struct stratix_cvp_conf *conf = mgr->priv; + struct pci_dev *pdev = conf->pci_dev; + int status = 0; + int delay_us, i; + u32 val32; + u16 status_msk; + u16 val16; + + /* + * STEP 12 - reset START_XFER bit + */ + pci_read_config_dword(pdev, VSEC_CVP_PROG_CTRL, &val32); + val32 &= ~VSEC_CVP_PROG_CTRL_START_XFER; + pci_write_config_dword(pdev, VSEC_CVP_PROG_CTRL, val32); + + /* + * STEP 13 - reset CVP_CONFIG bit + */ + val32 &= ~VSEC_CVP_PROG_CTRL_CONFIG; + pci_write_config_dword(pdev, VSEC_CVP_PROG_CTRL, val32); + + /* + * STEP 14 + * - set CVP_NUMCLKS to 0x01 and then issue CVP_DUMMY_WR dummy + * writes to the HIP + */ + pci_read_config_dword(pdev, VSEC_CVP_MODE_CTRL, &val32); + val32 &= ~VSEC_CVP_MODE_CTRL_NUMCLKS; + /* set number of CVP clock cycles for every CVP Data Register Write */ + val32 |= 0x01 << 8; /* 1 clock */ + pci_write_config_dword(pdev, VSEC_CVP_MODE_CTRL, val32); + + /* dummy memory write to switch from CVP clock to internal clock */ + val32 = 0xdeadbeef; + if (conf->map) { + u32 *map = conf->map; + + for (i = 0; i < CVP_DUMMY_WR; i++) + *map++ = val32; /* use MemoryWrite */ + } else { + for (i = 0; i < CVP_DUMMY_WR; i++) + pci_write_config_dword(pdev, VSEC_CVP_DATA, val32); + } + + /* + * STEP 15 - poll CVP_CONFIG_READY bit + */ + delay_us = 0; + while (1) { + pci_read_config_word(pdev, VSEC_CVP_STATUS, &val16); + if ((val16 & VSEC_CVP_STATUS_CFG_RDY) == 0) + break; + + udelay(1); /* wait 1us */ + + if (delay_us++ > TIMEOUT_IN_US) { + dev_warn(&mgr->dev, "CVP_CONFIG_READY == 0 timeout\n"); + status = -ETIME; + break; + } + } + + if (status < 0) + return status; + + /* + * STEP 16 - check CVP_CONFIG_ERROR_LATCHED bit + */ + pci_read_config_dword(pdev, VSEC_UNCOR_ERR_STATUS, &val32); + if (val32 & VSEC_UNCOR_ERR_CVP_CFG_ERR) { + dev_err(&mgr->dev, " detected CVP_CONFIG_ERROR_LATCHED!\n"); + return -EFAULT; + } + + /* + * STEP 17 - reset CVP_MODE and HIP_CLK_SEL bit + */ + pci_read_config_dword(pdev, VSEC_CVP_MODE_CTRL, &val32); + val32 &= ~VSEC_CVP_MODE_CTRL_HIP_CLK_SEL; + val32 &= ~VSEC_CVP_MODE_CTRL_CVPMODE; + pci_write_config_dword(pdev, VSEC_CVP_MODE_CTRL, val32); + + /* + * STEP 18 - poll PLD_CLK_IN_USE and USER_MODE bits + */ + delay_us = 0; + status_msk = VSEC_CVP_STATUS_PLD_CLK_IN_USE | VSEC_CVP_STATUS_USERMODE; + while (1) { + pci_read_config_word(pdev, VSEC_CVP_STATUS, &val16); + if ((val16 & status_msk) == status_msk) + break; + + udelay(1); /* wait 1us */ + + if (delay_us++ > TIMEOUT_IN_US) { + dev_warn(&mgr->dev, "PLD_CLK_IN_USE|USERMODE timeout\n"); + status = -ETIME; + break; + } + } + + return status; +} + +static const struct fpga_manager_ops stratix_cvp_ops = { + .state = stratix_cvp_state, + .write_init = stratix_cvp_write_init, + .write = stratix_cvp_write, + .write_complete = stratix_cvp_write_complete, +}; + +static int stratix_cvp_probe(struct pci_dev *pdev, + const struct pci_device_id *dev_id) +{ + struct stratix_cvp_conf *conf; + u16 cmd, val16; + int ret; + + /* Enable memory BAR access */ + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MEMORY)) { + cmd |= PCI_COMMAND_MEMORY; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + } + + conf = devm_kzalloc(&pdev->dev, sizeof(*conf), GFP_KERNEL); + if (!conf) { + ret = -ENOMEM; + goto err; + } + + pci_bus_read_config_word(pdev->bus, FNC, VSEC_PCIE_EXT_CAP_ID, &val16); + if (val16 != VSEC_PCIE_EXT_CAP_ID_VAL) { + dev_err(&pdev->dev, "Wrong EXT_CAP_ID value 0x%x\n", val16); + ret = -ENODEV; + goto err; + } + + conf->pci_dev = pdev; + conf->map_base = pci_resource_start(pdev, CVP_BAR); + conf->map_len = pci_resource_len(pdev, CVP_BAR); + + if (pci_request_region(pdev, CVP_BAR, "CVP") < 0) { + dev_err(&pdev->dev, "Requesting CVP BAR region failed\n"); + ret = -ENODEV; + goto err; + } + + conf->map = ioremap_nocache(conf->map_base, conf->map_len); + if (!conf->map) + dev_warn(&pdev->dev, "Mapping CVP BAR failed\n"); + + ret = fpga_mgr_register(&pdev->dev, STRATIX_V_CVP_MGR_NAME, + &stratix_cvp_ops, conf); + if (ret) + goto err_unmap; + + conf->mgr = fpga_mgr_get(&pdev->dev); + if (IS_ERR(conf->mgr)) { + dev_err(&pdev->dev, "Getting fpga mgr reference failed\n"); + ret = -ENODEV; + goto err_unmap; + } + + ret = fpga_mgr_firmware_load(conf->mgr, NULL, firmware); + if (ret) { + dev_err(&pdev->dev, "failed to load fpga image\n"); + goto err_mgr; + } + + return 0; + +err_mgr: + fpga_mgr_put(conf->mgr); + fpga_mgr_unregister(&pdev->dev); +err_unmap: + iounmap(conf->map); + pci_release_region(pdev, CVP_BAR); +err: + cmd &= ~PCI_COMMAND_MEMORY; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + return ret; +} + +static void stratix_cvp_remove(struct pci_dev *pdev) +{ + struct fpga_manager *mgr = pci_get_drvdata(pdev); + struct stratix_cvp_conf *conf = mgr->priv; + u16 cmd; + + fpga_mgr_put(conf->mgr); + fpga_mgr_unregister(&pdev->dev); + iounmap(conf->map); + pci_release_region(pdev, CVP_BAR); + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + cmd &= ~PCI_COMMAND_MEMORY; + pci_write_config_word(pdev, PCI_COMMAND, cmd); +} + +#define PCI_VENDOR_ID_ALTERA 0x1172 +#define PCI_DEVICE_ID_FPGA 0xe001 + +static struct pci_device_id stratix_cvp_id_tbl[] = { + { PCI_VDEVICE(ALTERA, PCI_DEVICE_ID_FPGA) }, + { } +}; + +static struct pci_driver stratix_cvp_driver = { + .name = DRV_NAME, + .id_table = stratix_cvp_id_tbl, + .probe = stratix_cvp_probe, + .remove = stratix_cvp_remove, +}; + +module_pci_driver(stratix_cvp_driver) + +MODULE_DEVICE_TABLE(pci, stratix_cvp_id_tbl); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anatolij Gustschin "); +MODULE_DESCRIPTION("Module to load Altera FPGA over CvP");