Re: [PATCH v3 2/2] fpga manager: Add Xilinx slave serial SPI driver
From: Michal Simek <hidden>
Date: 2017-02-22 15:04:47
On 22.2.2017 11:39, Anatolij Gustschin wrote:
quoted hunk
The driver loads FPGA firmware over SPI, using the "slave serial" configuration interface on Xilinx FPGAs. Signed-off-by: Anatolij Gustschin <redacted> --- This patch requires patch https://lkml.org/lkml/2017/2/15/667 for building Changes in v3: - use named constant for udelay()/usleep_range() arguments - drop not needed .owner init - correct module licence (GPL v2) - fix build warning with newer gcc (in min() macro) Changes in v2: - rebased on v4.10 drivers/fpga/Kconfig | 7 ++ drivers/fpga/Makefile | 1 + drivers/fpga/xilinx-spi.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 drivers/fpga/xilinx-spi.cdiff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index ce861a2..6b42786 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_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_TESTdiff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index 8df07bc..7582c5a 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_XILINX_SPI) += xilinx-spi.o obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o # FPGA Bridge Driversdiff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c new file mode 100644 index 0000000..af95727 --- /dev/null +++ b/drivers/fpga/xilinx-spi.c@@ -0,0 +1,185 @@ +/* + * Xilinx Spartan6 Slave Serial SPI Driver + * + * Copyright (C) 2017 DENX Software Engineering + * + * Anatolij Gustschin <agust-ynQEQJNshbs@public.gmane.org> + * + * 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 <linux/delay.h> +#include <linux/device.h> +#include <linux/fpga/fpga-mgr.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/of.h> +#include <linux/spi/spi.h> +#include <linux/sizes.h> + +#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; + const size_t prog_latency_1000us = 1000; + const size_t prog_pulse_1us = 1; + + 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(prog_pulse_1us); /* 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(prog_latency_1000us, prog_latency_1000us + 500);
Based on my colleague for spartan6 this should be 5ms and other xilinx fpgas can take up to 7.5ms. In general, it is better to poll for INIT_B pin==High (but that takes another GPIO) and wait until at least 10 ms before timeout.
+ 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) {
+ size_t remaining, stride;
+ int ret;
+
+ remaining = fw_data_end - fw_data;
+ stride = min_t(size_t, remaining, 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;
+ }
+
Based on my colleague:
IMHO, for step 4 it is better to do these steps to cover more possible
configuration DONE scenarios:
While ((DONE==0) and (NOT TIMEOUT)) {
Continue to apply CCLK cycles;
Check DONE;
timeout++;
}
Apply at least 8 more CCLK cycles.
The timeout should be equivalent to at least 10 ms of time.
Thanks,
Michal
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html