From: iansdannapel@gmail.com
To: linux-fpga@vger.kernel.org
Cc: "Moritz Fischer" <mdf@kernel.org>, "Wu Hao" <hao.wu@intel.com>,
"Xu Yilun" <yilun.xu@intel.com>, "Tom Rix" <trix@redhat.com>,
"Rob Herring" <robh@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Neil Armstrong" <neil.armstrong@linaro.org>,
"Jonathan Cameron" <Jonathan.Cameron@huawei.com>,
"Rafał Miłecki" <rafal@milecki.pl>,
"Aradhya Bhatia" <a-bhatia1@ti.com>,
"Ian Dannapel" <iansdannapel@gmail.com>,
devicetree@vger.kernel.org (open list:OPEN FIRMWARE AND
FLATTENED DEVICE TREE BINDINGS),
linux-kernel@vger.kernel.org (open list)
Subject: [v4 3/3] fpga-mgr: Add Efinix SPI programming driver
Date: Fri, 28 Feb 2025 10:47:32 +0100 [thread overview]
Message-ID: <20250228094732.54642-4-iansdannapel@gmail.com> (raw)
In-Reply-To: <20250228094732.54642-1-iansdannapel@gmail.com>
From: Ian Dannapel <iansdannapel@gmail.com>
Add a new driver for loading binary firmware to configuration
RAM using "SPI passive mode" on Efinix FPGAs.
Signed-off-by: Ian Dannapel <iansdannapel@gmail.com>
---
drivers/fpga/Kconfig | 7 ++
drivers/fpga/Makefile | 1 +
drivers/fpga/efinix-spi.c | 212 ++++++++++++++++++++++++++++++++++++++
3 files changed, 220 insertions(+)
create mode 100644 drivers/fpga/efinix-spi.c
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 37b35f58f0df..b5d60ba62900 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -83,6 +83,13 @@ config FPGA_MGR_XILINX_SPI
FPGA manager driver support for Xilinx FPGA configuration
over slave serial interface.
+config FPGA_MGR_EFINIX_SPI
+ tristate "Efinix FPGA configuration over SPI"
+ depends on SPI
+ help
+ FPGA manager driver support for Efinix FPGAs configuration over SPI
+ (passive mode only).
+
config FPGA_MGR_ICE40_SPI
tristate "Lattice iCE40 SPI"
depends on OF && SPI
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index aeb89bb13517..adbd51d2cd1e 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o
obj-$(CONFIG_FPGA_MGR_XILINX_CORE) += xilinx-core.o
obj-$(CONFIG_FPGA_MGR_XILINX_SELECTMAP) += xilinx-selectmap.o
obj-$(CONFIG_FPGA_MGR_XILINX_SPI) += xilinx-spi.o
+obj-$(CONFIG_FPGA_MGR_EFINIX_SPI) += efinix-spi.o
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
obj-$(CONFIG_FPGA_MGR_ZYNQMP_FPGA) += zynqmp-fpga.o
obj-$(CONFIG_FPGA_MGR_VERSAL_FPGA) += versal-fpga.o
diff --git a/drivers/fpga/efinix-spi.c b/drivers/fpga/efinix-spi.c
new file mode 100644
index 000000000000..07885110a8a8
--- /dev/null
+++ b/drivers/fpga/efinix-spi.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * FPGA Manager Driver for Efinix
+ *
+ * Copyright (C) 2025 iris-GmbH infrared & intelligent sensors
+ *
+ * Ian Dannapel <iansdannapel@gmail.com>
+ *
+ * Load Efinix FPGA firmware over SPI using the serial configuration interface.
+ *
+ * Note 1: Only passive mode (host initiates transfer) is currently supported.
+ * Note 2: Topaz and Titanium support is based on documentation but remains
+ * untested.
+ */
+
+#include <linux/delay.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+
+struct efinix_spi_conf {
+ struct spi_device *spi;
+ struct gpio_desc *cdone;
+ struct gpio_desc *reset;
+};
+
+static void efinix_spi_reset(struct efinix_spi_conf *conf)
+{
+ gpiod_set_value(conf->reset, 1);
+ /* tCRESET_N > 320 ns */
+ usleep_range(1, 2);
+ gpiod_set_value(conf->reset, 0);
+
+ /* tDMIN > 32 us */
+ usleep_range(35, 40);
+}
+
+static enum fpga_mgr_states efinix_spi_state(struct fpga_manager *mgr)
+{
+ struct efinix_spi_conf *conf = mgr->priv;
+
+ if (conf->cdone && gpiod_get_value(conf->cdone) == 1)
+ return FPGA_MGR_STATE_OPERATING;
+
+ return FPGA_MGR_STATE_UNKNOWN;
+}
+
+static int efinix_spi_write_init(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ const char *buf, size_t count)
+{
+ if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
+ dev_err(&mgr->dev, "Partial reconfiguration not supported\n");
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int efinix_spi_write(struct fpga_manager *mgr, const char *buf,
+ size_t count)
+{
+ struct efinix_spi_conf *conf = mgr->priv;
+ int ret;
+ struct spi_message message;
+ struct spi_transfer assert_cs = {
+ .cs_change = 1
+ };
+ struct spi_transfer write_xfer = {
+ .tx_buf = buf,
+ .len = count
+ };
+ struct spi_transfer clk_cycles = {
+ .len = 13, // > 100 clock cycles
+ .tx_buf = NULL
+ };
+ u8 *dummy_buf;
+
+ dummy_buf = kzalloc(13, GFP_KERNEL);
+ if (!dummy_buf) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ spi_bus_lock(conf->spi->controller);
+ spi_message_init(&message);
+ spi_message_add_tail(&assert_cs, &message);
+ ret = spi_sync_locked(conf->spi, &message);
+ if (ret)
+ goto fail_unlock;
+
+ /* reset with asserted cs */
+ efinix_spi_reset(conf);
+
+ spi_message_init(&message);
+ spi_message_add_tail(&write_xfer, &message);
+
+ clk_cycles.tx_buf = dummy_buf;
+ spi_message_add_tail(&clk_cycles, &message);
+
+ ret = spi_sync_locked(conf->spi, &message);
+ if (ret)
+ dev_err(&mgr->dev, "SPI error in firmware write: %d\n", ret);
+
+fail_unlock:
+ spi_bus_unlock(conf->spi->controller);
+ kfree(dummy_buf);
+fail:
+ return ret;
+}
+
+static int efinix_spi_write_complete(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
+{
+ struct efinix_spi_conf *conf = mgr->priv;
+ unsigned long timeout =
+ jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
+ bool expired = false;
+ int done;
+
+ if (conf->cdone) {
+ while (!expired) {
+ expired = time_after(jiffies, timeout);
+
+ done = gpiod_get_value(conf->cdone);
+ if (done < 0)
+ return done;
+
+ if (done)
+ break;
+ }
+ }
+
+ if (expired)
+ return -ETIMEDOUT;
+
+ /* tUSER > 25 us */
+ usleep_range(30, 35);
+ return 0;
+}
+
+static const struct fpga_manager_ops efinix_spi_ops = {
+ .state = efinix_spi_state,
+ .write_init = efinix_spi_write_init,
+ .write = efinix_spi_write,
+ .write_complete = efinix_spi_write_complete,
+};
+
+static int efinix_spi_probe(struct spi_device *spi)
+{
+ struct efinix_spi_conf *conf;
+ struct fpga_manager *mgr;
+
+ conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
+ if (!conf)
+ return -ENOMEM;
+
+ conf->spi = spi;
+
+ conf->reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(conf->reset))
+ return dev_err_probe(&spi->dev, PTR_ERR(conf->reset),
+ "Failed to get RESET gpio\n");
+
+ if (!(spi->mode & SPI_CPHA) || !(spi->mode & SPI_CPOL))
+ return dev_err_probe(&spi->dev, -EINVAL,
+ "Unsupported SPI mode, set CPHA and CPOL\n");
+
+ conf->cdone = devm_gpiod_get_optional(&spi->dev, "cdone", GPIOD_IN);
+ if (IS_ERR(conf->cdone))
+ return dev_err_probe(&spi->dev, PTR_ERR(conf->cdone),
+ "Failed to get CDONE gpio\n");
+
+ mgr = devm_fpga_mgr_register(&spi->dev,
+ "Efinix FPGA Manager",
+ &efinix_spi_ops, conf);
+
+ return PTR_ERR_OR_ZERO(mgr);
+}
+
+static const struct of_device_id efinix_spi_of_match[] = {
+ { .compatible = "efinix,trion-spi", },
+ { .compatible = "efinix,titanium-spi", },
+ { .compatible = "efinix,topaz-spi", },
+ { .compatible = "efinix,fpga-spi", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, efinix_spi_of_match);
+
+static const struct spi_device_id efinix_ids[] = {
+ { "trion-spi", 0 },
+ { "titanium-spi", 0 },
+ { "topaz-spi", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(spi, efinix_ids);
+
+static struct spi_driver efinix_spi_driver = {
+ .driver = {
+ .name = "efinix-spi",
+ .of_match_table = efinix_spi_of_match,
+ },
+ .probe = efinix_spi_probe,
+ .id_table = efinix_ids,
+};
+
+module_spi_driver(efinix_spi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ian Dannapel <iansdannapel@gmail.com>");
+MODULE_DESCRIPTION("Efinix FPGA SPI Programming Driver (Topaz/Titanium untested)");
--
2.43.0
next prev parent reply other threads:[~2025-02-28 9:47 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-02-28 9:47 [v4 0/3] Add Efinix FPGA SPI programming support iansdannapel
2025-02-28 9:47 ` [v4 1/3] dt-bindings: vendor-prefix: Add prefix for Efinix, Inc iansdannapel
2025-02-28 18:29 ` Conor Dooley
2025-03-01 13:10 ` Krzysztof Kozlowski
2025-03-04 11:24 ` Alexander Dahl
2025-02-28 9:47 ` [v4 2/3] dt-bindings: fpga: Add Efinix SPI programming bindings iansdannapel
2025-02-28 18:28 ` Conor Dooley
2025-03-03 10:10 ` Ian Dannapel
2025-03-03 10:29 ` Ian Dannapel
2025-03-03 10:33 ` Krzysztof Kozlowski
2025-03-03 10:31 ` Conor Dooley
2025-03-03 10:34 ` Krzysztof Kozlowski
2025-03-01 13:13 ` Krzysztof Kozlowski
2025-02-28 9:47 ` iansdannapel [this message]
2025-03-03 11:57 ` [v4 3/3] fpga-mgr: Add Efinix SPI programming driver Manne, Nava kishore
2025-03-03 12:16 ` Ian Dannapel
2025-03-16 15:03 ` Xu Yilun
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250228094732.54642-4-iansdannapel@gmail.com \
--to=iansdannapel@gmail.com \
--cc=Jonathan.Cameron@huawei.com \
--cc=a-bhatia1@ti.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=hao.wu@intel.com \
--cc=krzk+dt@kernel.org \
--cc=linux-fpga@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mdf@kernel.org \
--cc=neil.armstrong@linaro.org \
--cc=rafal@milecki.pl \
--cc=robh@kernel.org \
--cc=trix@redhat.com \
--cc=yilun.xu@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.