From patchwork Thu Feb 9 10:00:39 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anatolij Gustschin X-Patchwork-Id: 9564317 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 E2E31601C3 for ; Thu, 9 Feb 2017 10:00:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BD18528520 for ; Thu, 9 Feb 2017 10:00:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B0CE528524; Thu, 9 Feb 2017 10:00:51 +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 9F63128520 for ; Thu, 9 Feb 2017 10:00:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752199AbdBIKAt (ORCPT ); Thu, 9 Feb 2017 05:00:49 -0500 Received: from mail-out.m-online.net ([212.18.0.10]:51161 "EHLO mail-out.m-online.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752176AbdBIKAs (ORCPT ); Thu, 9 Feb 2017 05:00:48 -0500 Received: from frontend01.mail.m-online.net (unknown [192.168.8.182]) by mail-out.m-online.net (Postfix) with ESMTP id 3vJtqy3Y5wz3hnMn; Thu, 9 Feb 2017 11:00:46 +0100 (CET) Received: from localhost (dynscan1.mnet-online.de [192.168.6.68]) by mail.m-online.net (Postfix) with ESMTP id 3vJtqy2BpjzvkW6; Thu, 9 Feb 2017 11:00:46 +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 l-gpSKR0mJj8; Thu, 9 Feb 2017 11:00:43 +0100 (CET) X-Auth-Info: vICAYftBbaDNZDF8Ca7qDhm6ys96ITNO6BhkqmHCTaQ= Received: from crub.agik.hopto.org (p4FCB6C75.dip0.t-ipconnect.de [79.203.108.117]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.mnet-online.de (Postfix) with ESMTPSA; Thu, 9 Feb 2017 11:00:43 +0100 (CET) From: Anatolij Gustschin To: linux-fpga@vger.kernel.org Cc: Alan Tull , Moritz Fischer Subject: [PATCH] fpga: Add driver for Arria10 partial reconfiguration over PCIe Date: Thu, 9 Feb 2017 11:00:39 +0100 Message-Id: <1486634439-7892-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 Partial reconfiguration (PR) allows to reconfigure part of the design's core logic while the remainder of the design continues running. This driver loads a new design (RBF file under /lib/firmware) over PCIe. Writing new rbf file name to /sys/class/fpga_manager/fpgaN/firmware interface triggers FPGA reconfiguration. Signed-off-by: Anatolij Gustschin --- drivers/fpga/Kconfig | 7 ++ drivers/fpga/Makefile | 1 + drivers/fpga/a10-pcie-pr.c | 299 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+) create mode 100644 drivers/fpga/a10-pcie-pr.c diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index ce861a2..aa50fa4 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -20,6 +20,13 @@ config FPGA_REGION FPGA Regions allow loading FPGA images under control of the Device Tree. +config FPGA_MGR_A10_PCIE_PR + tristate "Arria 10 partial reconfiguration over PCIe PR IP" + depends on PCI + help + FPGA manager driver support for Arria 10 partial + reconfiguration over PCIe + config FPGA_MGR_SOCFPGA tristate "Altera SOCFPGA FPGA Manager" depends on ARCH_SOCFPGA || COMPILE_TEST diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index 8df07bc..b90b3f5 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o # FPGA Manager Drivers +obj-$(CONFIG_FPGA_MGR_A10_PCIE_PR) += a10-pcie-pr.o obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o diff --git a/drivers/fpga/a10-pcie-pr.c b/drivers/fpga/a10-pcie-pr.c new file mode 100644 index 0000000..09de181 --- /dev/null +++ b/drivers/fpga/a10-pcie-pr.c @@ -0,0 +1,299 @@ +/* + * FPGA Manager Driver for Arria 10 partial reconfiguration over PCIe. + * Firmware must be in binary "rbf" format. + * + * Copyright (C) 2017 DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "a10-pcie-pr" +#define PCIE_PR_MGR_NAME "PCIe PR Manager" +#define DFLT_FIRMWARE_STR "a10_pr_region.rbf" +#define PR_IP_BAR 4 /* PCIe PR IP core BAR */ +#define PR_CONTROLLER_OFFSET 0xcfb0 +#define CMD_PR 0x01 +#define CMD_DOUBLE_PR 0x03 + +static char firmware[NAME_MAX]; +module_param_string(firmware, firmware, sizeof(firmware), 0444); +MODULE_PARM_DESC(firmware, " RBF file in /lib/firmware to load via PCIe PR"); + +/* use value 1 for debugging only */ +static int chkcfg; +module_param(chkcfg, int, 0664); +MODULE_PARM_DESC(chkcfg, " 1 - check error status, 0 - skip check (default 0)"); + +/* PR IP core registers */ +struct pr_regs { + u32 pr_data; + u32 pr_csr; +}; + +struct pcie_pr_conf { + struct fpga_manager *mgr; + struct pci_dev *pci_dev; + struct pr_regs *regs; + void __iomem *map; + u32 fw_size; +}; + +static enum fpga_mgr_states pcie_pr_state(struct fpga_manager *mgr) +{ + return mgr->state; +} + +static int pcie_pr_write_init(struct fpga_manager *mgr, + struct fpga_image_info *info, + const char *buf, size_t count) +{ + struct pcie_pr_conf *conf = mgr->priv; + u32 status; + + if (info) + dev_dbg(&mgr->dev, "%s(): flags 0x%x\n", __func__, info->flags); + + if (info && !(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) { + dev_err(&mgr->dev, "Only partial reconfiguration supported.\n"); + return -EINVAL; + } + + status = readl(&conf->regs->pr_csr); + dev_dbg(&mgr->dev, "PR IP status 0x%08X\n", status); + + dev_dbg(&mgr->dev, "PR IP command 0x%08X\n", CMD_PR); + writel(CMD_PR, &conf->regs->pr_csr); + + status = readl(&conf->regs->pr_csr); + dev_dbg(&mgr->dev, "PR IP status: 0x%08X\n", (int) status); + if ((status != 0x10) && (status != 0x0)) { + dev_err(&mgr->dev, "status 0x%x\n", status); + return -EFAULT; + } + + return 0; +} + +static int pcie_pr_write(struct fpga_manager *mgr, const char *buf, + size_t count) +{ + struct pcie_pr_conf *conf = mgr->priv; + const u32 *data, *data_end; + size_t bytes = 0; + int ret = 0; + int status; + + dev_dbg(&mgr->dev, "%s: count %li\n", __func__, count); + + conf->fw_size = 0; + data = (u32 *)buf; + data_end = (u32 *)((char *)buf + count); + + while (data < data_end) { + writel(*data++, &conf->regs->pr_data); + bytes += 4; + if (chkcfg) { + status = readl(&conf->regs->pr_csr) >> 2; + switch (status) { + case 1 ... 3: + dev_err(&mgr->dev, "PR error: 0x%x\n", status); + ret = -EIO; + break; + } + } + } + + dev_dbg(&mgr->dev, "PR bytes written: %li\n", bytes); + + if (!ret) { + /* + * remember the loaded rbf image size to show this + * info via sysfs firmware file when reading it + */ + conf->fw_size = count; + } + + return ret; +} + +static int pcie_pr_write_complete(struct fpga_manager *mgr, + struct fpga_image_info *info) +{ + struct pcie_pr_conf *conf = mgr->priv; + int status = 0; + + dev_dbg(&mgr->dev, "%s\n", __func__); + + status = readl(&conf->regs->pr_csr); + if (status == 0x14) { + dev_dbg(&mgr->dev, "PR done: 0x%08X\n", status); + return 0; + } + + dev_err(&mgr->dev, "PR error: 0x%08X\n", status); + + return -EIO; +} + +static const struct fpga_manager_ops pcie_pr_ops = { + .state = pcie_pr_state, + .write_init = pcie_pr_write_init, + .write = pcie_pr_write, + .write_complete = pcie_pr_write_complete, +}; + +static ssize_t show_firmware(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fpga_manager *mgr = to_fpga_manager(dev); + struct pcie_pr_conf *conf = mgr->priv; + + return snprintf(buf, PAGE_SIZE, "%s %i\n", + firmware, conf->fw_size); +} + +static ssize_t store_firmware(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fpga_manager *mgr = to_fpga_manager(dev); + int ret; + + if (!count || count > NAME_MAX) + return -EINVAL; + + ret = sscanf(buf, "%s", firmware); + if (ret != 1) + return -EINVAL; + + ret = fpga_mgr_firmware_load(mgr, NULL, firmware); + + return ret ? ret : count; +} + +static DEVICE_ATTR(firmware, 0664, show_firmware, store_firmware); + +static int pcie_pr_probe(struct pci_dev *pdev, + const struct pci_device_id *dev_id) +{ + struct pcie_pr_conf *conf; + int ret; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + ret = pci_enable_device(pdev); + if (ret < 0) { + dev_err(&pdev->dev, "can't enable pci device: %d\n", ret); + return ret; + } + + pci_set_master(pdev); + + conf = devm_kzalloc(&pdev->dev, sizeof(*conf), GFP_KERNEL); + if (!conf) { + ret = -ENOMEM; + goto err; + } + + conf->pci_dev = pdev; + + if (pci_request_region(pdev, PR_IP_BAR, "PR IP") < 0) { + dev_err(&pdev->dev, "Requesting PR BAR region failed\n"); + ret = -ENODEV; + goto err; + } + + conf->map = pci_iomap(pdev, PR_IP_BAR, 0); + if (!conf->map) { + dev_warn(&pdev->dev, "Mapping PR BAR failed\n"); + ret = -ENODEV; + goto err_reg; + } + + conf->regs = conf->map + PR_CONTROLLER_OFFSET; + + ret = fpga_mgr_register(&pdev->dev, PCIE_PR_MGR_NAME, + &pcie_pr_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_mgr; + } + + if (!strnlen(firmware, NAME_MAX)) + strncpy(firmware, DFLT_FIRMWARE_STR, sizeof(firmware)); + + ret = fpga_mgr_firmware_load(conf->mgr, NULL, firmware); + if (ret) + dev_warn(&pdev->dev, "loading fpga image failed\n"); + + ret = device_create_file(&conf->mgr->dev, &dev_attr_firmware); + if (ret) + dev_warn(&pdev->dev, "can't create reconfig interface %d\n", + ret); + + return 0; + +err_mgr: + fpga_mgr_unregister(&pdev->dev); +err_unmap: + pci_iounmap(pdev, conf->map); +err_reg: + pci_release_region(pdev, PR_IP_BAR); +err: + pci_disable_device(pdev); + return ret; +} + +static void pcie_pr_remove(struct pci_dev *pdev) +{ + struct fpga_manager *mgr = pci_get_drvdata(pdev); + struct pcie_pr_conf *conf = mgr->priv; + + fpga_mgr_put(conf->mgr); + fpga_mgr_unregister(&pdev->dev); + pci_iounmap(pdev, conf->map); + pci_release_region(pdev, PR_IP_BAR); + pci_disable_device(pdev); +} + +#define PCI_VENDOR_ID_ALTERA 0x1172 +#define PCI_DEVICE_ID_FPGA 0xe003 + +static struct pci_device_id pcie_pr_id_tbl[] = { + { PCI_VDEVICE(ALTERA, PCI_DEVICE_ID_FPGA) }, + { } +}; + +static struct pci_driver pcie_pr_driver = { + .name = DRV_NAME, + .id_table = pcie_pr_id_tbl, + .probe = pcie_pr_probe, + .remove = pcie_pr_remove, +}; + +module_pci_driver(pcie_pr_driver) + +MODULE_DEVICE_TABLE(pci, pcie_pr_id_tbl); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anatolij Gustschin "); +MODULE_DESCRIPTION("Arria 10 FPGA reconfiguration via PCIe PR IP");