From patchwork Thu Feb 16 15:11:00 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anatolij Gustschin X-Patchwork-Id: 9577407 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 6750D60209 for ; Thu, 16 Feb 2017 15:11:10 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5661128604 for ; Thu, 16 Feb 2017 15:11:10 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4B34128614; Thu, 16 Feb 2017 15:11:10 +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 BE2B328604 for ; Thu, 16 Feb 2017 15:11:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755201AbdBPPLJ (ORCPT ); Thu, 16 Feb 2017 10:11:09 -0500 Received: from mail-out.m-online.net ([212.18.0.9]:38150 "EHLO mail-out.m-online.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754996AbdBPPLI (ORCPT ); Thu, 16 Feb 2017 10:11:08 -0500 Received: from frontend01.mail.m-online.net (unknown [192.168.8.182]) by mail-out.m-online.net (Postfix) with ESMTP id 3vPKNq092pz3hjlv; Thu, 16 Feb 2017 16:11:07 +0100 (CET) Received: from localhost (dynscan01.mnet-online.de [192.168.6.70]) by mail.m-online.net (Postfix) with ESMTP id 3vPKNp60K3zvkYQ; Thu, 16 Feb 2017 16:11:06 +0100 (CET) X-Virus-Scanned: amavisd-new at mnet-online.de Received: from mail.mnet-online.de ([192.168.8.182]) by localhost (dynscan01.mail.m-online.net [192.168.6.70]) (amavisd-new, port 10024) with ESMTP id LmqWtPbmPvcj; Thu, 16 Feb 2017 16:11:05 +0100 (CET) X-Auth-Info: A4OmfKy+UadCNrAe+VLHObGb9koT1ec+VUwdyX8iffM= Received: from crub.agik.hopto.org (p4FCB7738.dip0.t-ipconnect.de [79.203.119.56]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.mnet-online.de (Postfix) with ESMTPSA; Thu, 16 Feb 2017 16:11:05 +0100 (CET) From: Anatolij Gustschin To: linux-fpga@vger.kernel.org, devicetree@vger.kernel.org Cc: Alan Tull , Moritz Fischer , Rob Herring , Mark Rutland Subject: [PATCH 2/2] fpga manager: Add Xilinx slave serial SPI driver Date: Thu, 16 Feb 2017 16:11:00 +0100 Message-Id: <1487257860-31115-3-git-send-email-agust@denx.de> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1487257860-31115-1-git-send-email-agust@denx.de> References: <1487257860-31115-1-git-send-email-agust@denx.de> 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 The driver loads FPGA firmware over SPI, using the "slave serial" configuration interface on Xilinx FPGAs. Signed-off-by: Anatolij Gustschin Acked-by: Alan Tull --- drivers/fpga/Kconfig | 7 ++ drivers/fpga/Makefile | 1 + drivers/fpga/xilinx-spi.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 drivers/fpga/xilinx-spi.c diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index b41b5f2..0e820e7 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -40,6 +40,13 @@ config FPGA_MGR_SOCFPGA_A10 help FPGA manager driver support for Altera Arria10 SoCFPGA. +config FPGA_MGR_XILINX_SPI + tristate "Xilinx Configuration over Slave Serial (SPI)" + depends on SPI + help + FPGA manager driver support for Xilinx FPGA configuration + over slave serial interface. + 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 643e8fe..170070b 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -10,6 +10,7 @@ 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_STRATIX_CVP) += stratix-cvp.o +obj-$(CONFIG_FPGA_MGR_XILINX_SPI) += xilinx-spi.o obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o # FPGA Bridge Drivers diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c new file mode 100644 index 0000000..16b3fa2 --- /dev/null +++ b/drivers/fpga/xilinx-spi.c @@ -0,0 +1,181 @@ +/* + * Xilinx Spartan6 Slave Serial SPI Driver + * + * Copyright (C) 2017 DENX Software Engineering + * + * Anatolij Gustschin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * Manage Xilinx FPGA firmware that is loaded over SPI using + * the slave serial configuration interface. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FPGA_MIN_CFG_WAIT_US 10 + +struct xilinx_spi_conf { + struct spi_device *spi; + struct gpio_desc *prog_b; + struct gpio_desc *done; +}; + +static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr) +{ + struct xilinx_spi_conf *conf = mgr->priv; + + if (!gpiod_get_value(conf->done)) + return FPGA_MGR_STATE_RESET; + + return FPGA_MGR_STATE_UNKNOWN; +} + +static int xilinx_spi_write_init(struct fpga_manager *mgr, + struct fpga_image_info *info, + const char *buf, size_t count) +{ + struct xilinx_spi_conf *conf = mgr->priv; + + if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) { + dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); + return -EINVAL; + } + + gpiod_set_value(conf->prog_b, 1); + + udelay(1); /* min is 500 ns */ + + gpiod_set_value(conf->prog_b, 0); + + if (gpiod_get_value(conf->done)) { + dev_err(&mgr->dev, "Unexpected DONE pin state...\n"); + return -EIO; + } + + /* program latency */ + usleep_range(1000, 1500); + return 0; +} + +static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf, + size_t count) +{ + struct xilinx_spi_conf *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); + + 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 xilinx_spi_write_complete(struct fpga_manager *mgr, + struct fpga_image_info *info) +{ + struct xilinx_spi_conf *conf = mgr->priv; + int wait; + + if (gpiod_get_value(conf->done)) + return 0; + + wait = info->config_complete_timeout_us / FPGA_MIN_CFG_WAIT_US; + if (info->config_complete_timeout_us % FPGA_MIN_CFG_WAIT_US) + wait += 1; + + while (wait--) { + usleep_range(FPGA_MIN_CFG_WAIT_US, FPGA_MIN_CFG_WAIT_US + 5); + + if (gpiod_get_value(conf->done)) + return 0; + } + + dev_err(&mgr->dev, "Timeout after config data transfer.\n"); + return -ETIMEDOUT; +} + +static const struct fpga_manager_ops xilinx_spi_ops = { + .state = xilinx_spi_state, + .write_init = xilinx_spi_write_init, + .write = xilinx_spi_write, + .write_complete = xilinx_spi_write_complete, +}; + +static int xilinx_spi_probe(struct spi_device *spi) +{ + struct xilinx_spi_conf *conf; + + conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); + if (!conf) + return -ENOMEM; + + conf->spi = spi; + + /* PROGRAM_B is active low */ + conf->prog_b = devm_gpiod_get(&spi->dev, "prog_b", GPIOD_OUT_LOW); + if (IS_ERR(conf->prog_b)) { + dev_err(&spi->dev, "Failed to get PROGRAM_B gpio: %ld\n", + PTR_ERR(conf->prog_b)); + return PTR_ERR(conf->prog_b); + } + + conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN); + if (IS_ERR(conf->done)) { + dev_err(&spi->dev, "Failed to get DONE gpio: %ld\n", + PTR_ERR(conf->done)); + return PTR_ERR(conf->done); + } + + return fpga_mgr_register(&spi->dev, "Xilinx Slave Serial FPGA Manager", + &xilinx_spi_ops, conf); +} + +static int xilinx_spi_remove(struct spi_device *spi) +{ + fpga_mgr_unregister(&spi->dev); + + return 0; +} + +static const struct of_device_id xlnx_spi_of_match[] = { + { .compatible = "xlnx,fpga-slave-serial", }, + {} +}; +MODULE_DEVICE_TABLE(of, xlnx_spi_of_match); + +static struct spi_driver xilinx_slave_spi_driver = { + .driver = { + .name = "xlnx-slave-spi", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(xlnx_spi_of_match), + }, + .probe = xilinx_spi_probe, + .remove = xilinx_spi_remove, +}; + +module_spi_driver(xilinx_slave_spi_driver) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anatolij Gustschin "); +MODULE_DESCRIPTION("Load Xilinx FPGA firmware over SPI");