From patchwork Fri Dec 16 23:17:53 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Clayton X-Patchwork-Id: 9478651 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 6B245607EE for ; Fri, 16 Dec 2016 23:18:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 59DF4285DC for ; Fri, 16 Dec 2016 23:18:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4B68028681; Fri, 16 Dec 2016 23:18:44 +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.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID 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 9F438285DC for ; Fri, 16 Dec 2016 23:18:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1761027AbcLPXS0 (ORCPT ); Fri, 16 Dec 2016 18:18:26 -0500 Received: from mail-pf0-f194.google.com ([209.85.192.194]:33428 "EHLO mail-pf0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1762212AbcLPXSN (ORCPT ); Fri, 16 Dec 2016 18:18:13 -0500 Received: by mail-pf0-f194.google.com with SMTP id 144so4982501pfv.0; Fri, 16 Dec 2016 15:18:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=qiY6zGwLTVrqd/nDCu0izr7ESaMVxzkigEr6CqtJg2A=; b=iE8ul+AapxKXZAFAMsNoiDhELXljSdDFuw5O3I5bjcfgKorKbNJmrv87qgAS1fa/jb g2S8De1MCUB5oiIJ4FmcQpUfuo5IDeDS+eBRZbNkEu5qPGvmuv6qY24C6TdvR9BnOsaI h/Al9QTBCZKdI07ksooR4bvHfDqw1/MqVUnpci/q72zCzcxQPIToHz8Qj+KiSiYgOMHq u9h1EoTnZAho1d/93CctfPdAhukWfvFqR0nU7pjozBRoHABa/bT5bmVfYcPc5WhGNUTM bLXMYtCt+FxXriCLQwkDoxDRcNRiNpXPE301cOvCV7vs3FaxEMRnKrNQd0kL5Tzt6Ivs TDaw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:in-reply-to:references; bh=qiY6zGwLTVrqd/nDCu0izr7ESaMVxzkigEr6CqtJg2A=; b=kp/YhAqnCogFbkchmbB6aQ9nDK6MTaevqyxE6p5gtS8j6xeng47roTgHt9IGm1z+7Y PHtl3NznbV49ocSObP6uD1dRIq9rBR84mPJo8syByPmChbs4ejpFr0NX3uql3iu5kxOu 7raQTqjdJL6yA3BvCgBTUwACL7skkCwz3T27NhBBtKHmlUtEz1T83o3WmVpvXjE0qKpy 91sb7a6djTot6OV9va8QISRARCbxPksCdaU+28atURe1rMshhIb6zzzAynmR2UoB61mK qQ+4F83drydeKEf6UZIkuy1bBNWTBp1aWEOD1tFKCJBFbLpiqAixfHY1x39E0YVYc1c6 xyMQ== X-Gm-Message-State: AKaTC00/Fk9Ly4oN9S6n5xrqHEtZMLaSHIcplA/CyglVXRWPPij2gUe79sbUpMpOGbBVRA== X-Received: by 10.98.8.84 with SMTP id c81mr5409168pfd.114.1481930292033; Fri, 16 Dec 2016 15:18:12 -0800 (PST) Received: from jclayton-pc.columbia.uniwest.com (68-185-59-186.static.knwc.wa.charter.com. [68.185.59.186]) by smtp.gmail.com with ESMTPSA id e11sm14280165pgp.10.2016.12.16.15.18.09 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 16 Dec 2016 15:18:11 -0800 (PST) From: Joshua Clayton To: Alan Tull , Moritz Fischer , Russell King , Catalin Marinas , Will Deacon , Shawn Guo , Sascha Hauer , Fabio Estevam Cc: Mark Rutland , Rob Herring , Anatolij Gustschin , Joshua Clayton , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-fpga@vger.kernel.org Subject: [PATCH v6 4/5] fpga manager: Add cyclone-ps-spi driver for Altera FPGAs Date: Fri, 16 Dec 2016 15:17:53 -0800 Message-Id: X-Mailer: git-send-email 2.9.3 In-Reply-To: References: In-Reply-To: References: 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 cyclone-ps-spi loads FPGA firmware over spi, using the "passive serial" interface on Altera Cyclone FPGAS. This is one of the simpler ways to set up an FPGA at runtime. The signal interface is close to unidirectional spi with lsb first. Signed-off-by: Joshua Clayton --- drivers/fpga/Kconfig | 7 ++ drivers/fpga/Makefile | 1 + drivers/fpga/cyclone-ps-spi.c | 186 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 drivers/fpga/cyclone-ps-spi.c diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index ce861a2..e6c032d 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_CYCLONE_PS_SPI + tristate "Altera Cyclone FPGA Passive Serial over SPI" + depends on SPI + help + FPGA manager driver support for Altera Cyclone using the + passive serial interface over SPI + 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..a112bef 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_CYCLONE_PS_SPI) += cyclone-ps-spi.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/cyclone-ps-spi.c b/drivers/fpga/cyclone-ps-spi.c new file mode 100644 index 0000000..f9126f9 --- /dev/null +++ b/drivers/fpga/cyclone-ps-spi.c @@ -0,0 +1,186 @@ +/** + * Altera Cyclone Passive Serial SPI Driver + * + * Copyright (c) 2017 United Western Technologies, Corporation + * + * Joshua Clayton + * + * Manage Altera FPGA firmware that is loaded over spi using the passive + * serial configuration method. + * Firmware must be in binary "rbf" format. + * Works on Cyclone V. Should work on cyclone series. + * May work on other Altera FPGAs. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FPGA_RESET_TIME 50 /* time in usecs to trigger FPGA config */ +#define FPGA_MIN_DELAY 50 /* min usecs to wait for config status */ +#define FPGA_MAX_DELAY 1000 /* max usecs to wait for config status */ + +struct cyclonespi_conf { + struct gpio_desc *config; + struct gpio_desc *status; + struct spi_device *spi; +}; + +static const struct of_device_id of_ef_match[] = { + { .compatible = "altr,cyclone-ps-spi-fpga-mgr", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_ef_match); + +static enum fpga_mgr_states cyclonespi_state(struct fpga_manager *mgr) +{ + struct cyclonespi_conf *conf = (struct cyclonespi_conf *)mgr->priv; + + if (gpiod_get_value(conf->status)) + return FPGA_MGR_STATE_RESET; + + return FPGA_MGR_STATE_UNKNOWN; +} + +static int cyclonespi_write_init(struct fpga_manager *mgr, + struct fpga_image_info *info, + const char *buf, size_t count) +{ + struct cyclonespi_conf *conf = (struct cyclonespi_conf *)mgr->priv; + int i; + + if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) { + dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); + return -EINVAL; + } + + gpiod_set_value(conf->config, 1); + usleep_range(FPGA_RESET_TIME, FPGA_RESET_TIME + 20); + if (!gpiod_get_value(conf->status)) { + dev_err(&mgr->dev, "Status pin should be low.\n"); + return -EIO; + } + + gpiod_set_value(conf->config, 0); + for (i = 0; i < (FPGA_MAX_DELAY / FPGA_MIN_DELAY); i++) { + usleep_range(FPGA_MIN_DELAY, FPGA_MIN_DELAY + 20); + if (!gpiod_get_value(conf->status)) + return 0; + } + + dev_err(&mgr->dev, "Status pin not ready.\n"); + return -EIO; +} + +static void rev_buf(void *buf, size_t len) +{ + u32 *fw32 = (u32 *)buf; + const u32 *fw_end = (u32 *)(buf + len); + + /* set buffer to lsb first */ + while (fw32 < fw_end) { + *fw32 = bitrev8x4(*fw32); + fw32++; + } +} + +static int cyclonespi_write(struct fpga_manager *mgr, const char *buf, + size_t count) +{ + struct cyclonespi_conf *conf = (struct cyclonespi_conf *)mgr->priv; + const char *fw_data = buf; + const char *fw_data_end = fw_data + count; + + while (fw_data < fw_data_end) { + int ret; + size_t stride = min(fw_data_end - fw_data, SZ_4K); + + rev_buf((void *)fw_data, stride); + ret = spi_write(conf->spi, fw_data, stride); + if (ret) { + dev_err(&mgr->dev, "spi error in firmware write: %d\n", + ret); + return ret; + } + fw_data += stride; + } + + return 0; +} + +static int cyclonespi_write_complete(struct fpga_manager *mgr, + struct fpga_image_info *info) +{ + struct cyclonespi_conf *conf = (struct cyclonespi_conf *)mgr->priv; + + if (gpiod_get_value(conf->status)) { + dev_err(&mgr->dev, "Error during configuration.\n"); + return -EIO; + } + + return 0; +} + +static const struct fpga_manager_ops cyclonespi_ops = { + .state = cyclonespi_state, + .write_init = cyclonespi_write_init, + .write = cyclonespi_write, + .write_complete = cyclonespi_write_complete, +}; + +static int cyclonespi_probe(struct spi_device *spi) +{ + struct cyclonespi_conf *conf = devm_kzalloc(&spi->dev, sizeof(*conf), + GFP_KERNEL); + + if (!conf) + return -ENOMEM; + + conf->spi = spi; + conf->config = devm_gpiod_get(&spi->dev, "config", GPIOD_OUT_HIGH); + if (IS_ERR(conf->config)) { + dev_err(&spi->dev, "Failed to get config gpio: %ld\n", + PTR_ERR(conf->config)); + return PTR_ERR(conf->config); + } + + conf->status = devm_gpiod_get(&spi->dev, "status", GPIOD_IN); + if (IS_ERR(conf->status)) { + dev_err(&spi->dev, "Failed to get status gpio: %ld\n", + PTR_ERR(conf->status)); + return PTR_ERR(conf->status); + } + + return fpga_mgr_register(&spi->dev, + "Altera Cyclone PS SPI FPGA Manager", + &cyclonespi_ops, conf); +} + +static int cyclonespi_remove(struct spi_device *spi) +{ + fpga_mgr_unregister(&spi->dev); + + return 0; +} + +static struct spi_driver cyclonespi_driver = { + .driver = { + .name = "cyclone-ps-spi", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_ef_match), + }, + .probe = cyclonespi_probe, + .remove = cyclonespi_remove, +}; + +module_spi_driver(cyclonespi_driver) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joshua Clayton "); +MODULE_DESCRIPTION("Module to load Altera FPGA firmware over spi");