[PATCH 3/4] mtd/fpga: add fpga directory to mtd (with Cyclone 10)

u-boot at emagii.com u-boot at emagii.com
Sat Feb 11 11:07:42 CET 2023


From: Ulf Samuelsson <ulf at emagii.com>

Signed-off-by: Ulf Samuelsson <ulf at emagii.com>
---
 drivers/mtd/fpga/Kconfig      |  47 ++++++
 drivers/mtd/fpga/Makefile     |   6 +
 drivers/mtd/fpga/cyclone_10.c | 278 ++++++++++++++++++++++++++++++++++
 3 files changed, 331 insertions(+)
 create mode 100644 drivers/mtd/fpga/Kconfig
 create mode 100644 drivers/mtd/fpga/Makefile
 create mode 100644 drivers/mtd/fpga/cyclone_10.c

diff --git a/drivers/mtd/fpga/Kconfig b/drivers/mtd/fpga/Kconfig
new file mode 100644
index 0000000000..e3aa8c4522
--- /dev/null
+++ b/drivers/mtd/fpga/Kconfig
@@ -0,0 +1,47 @@
+menu "SPI FPGA Support"
+
+config DM_SPI_FPGA
+	bool "Enable Driver Model for FPGA configuration"
+	depends on DM && DM_SPI
+	imply SPI_FPGA
+	help
+	  Enable driver model for FPGAs configurable using SPI.
+	  This SPI FPGA interface
+	  (spi_fpga_probe(), spi_fpga_write(), etc.) is then
+	  implemented by the SPI FPGA uclass.
+	  There is one standard SPI FPGA driver which knows how to probe
+	  chips supported by U-Boot. The uclass interface is defined in
+	  include/spi_fpga.h
+	  SPI and SPI FPGA must be enabled together
+	  (it is not possible to use driver model for one and not the other).
+
+if DM_SPI_FPGA
+
+config SPI_FPGA_MTD
+	bool "SPI FPGA MTD support"
+	depends on MTD
+	help
+	  Enable the MTD support for the FPGA SPI Passive Serial,
+	  This allows mtd_write commands to load an FPGA using passive serial
+	  If unsure, say N
+
+config SPI_FPGA_INTEL
+	bool "Intel/Altera FPGA Passive Serial configuration using SPI"
+	help
+	  Add support for various Intel SPI FPGA chips
+
+config SPI_FPGA_XILINX
+	bool "Xilinx FPGA Passive Serial configuration using SPI"
+	help
+	  Add support for various Xilinx FPGA chips
+
+config SPI_FPGA_CYCLONE10
+	bool "Cyclone 10 SPI FPGA MTD support"
+	depends on SPI_FPGA_MTD && SPI_FPGA_INTEL
+	help
+	  Enable the MTD support for the Cyclone 10 FPGA
+	  If unsure, say N
+
+endif
+
+endmenu # menu "SPI FPGA Support"
diff --git a/drivers/mtd/fpga/Makefile b/drivers/mtd/fpga/Makefile
new file mode 100644
index 0000000000..2cf19fc7cf
--- /dev/null
+++ b/drivers/mtd/fpga/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2006
+# Wolfgang Denk, DENX Software Engineering, wd at denx.de.
+
+obj-$(CONFIG_SPI_FPGA_CYCLONE10) += cyclone_10.o
diff --git a/drivers/mtd/fpga/cyclone_10.c b/drivers/mtd/fpga/cyclone_10.c
new file mode 100644
index 0000000000..41e273211e
--- /dev/null
+++ b/drivers/mtd/fpga/cyclone_10.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MTD Driver for Passive Serial configuration of Cyclone 10
+ *
+ * Copyright (C) 2020 Bombardier Transportation
+ * Ulf Samuelsson <ext.ulf.samuelsson at rail.bombardier.com>
+ * Ulf Samuelsson <ulf at emagii.com>
+ *
+ */
+
+#include <common.h>
+#include <console.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdt_support.h>
+//#include <flash.h>
+#include <mtd.h>
+#include <malloc.h>
+#include <spi.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <linux/delay.h>
+#include <dm/device_compat.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * How many milliseconds from CONF_DONE high to enter user mode
+ * Datasheet says 650 us, Delay 2 ms to be safe...
+ */
+#define	USER_MODE_DELAY				2
+
+struct cyc10_plat {
+	struct udevice		*dev;
+	struct spi_slave	*spi;
+	char			name[8];
+	struct gpio_desc	nconfig;
+	struct gpio_desc	nstatus;
+	struct gpio_desc	conf_done;
+	struct gpio_desc	crc_error;
+	u32			cs;
+	int			flags;
+	int			config_size;
+};
+
+static inline void write_nCONFIG(struct cyc10_plat *fpga, int value)
+{
+	dm_gpio_set_value(&fpga->nconfig, value);
+}
+
+static inline int read_nSTATUS(struct cyc10_plat *fpga)
+{
+	int val = dm_gpio_get_value(&fpga->nstatus);
+	if (val < 0) {
+		printf("%s: Failure reading nSTATUS; error=%d\n", fpga->name, val);
+	}
+	return val;
+}
+
+static inline int read_CONFIG_DONE(struct cyc10_plat *fpga)
+{
+	int val = dm_gpio_get_value(&fpga->conf_done);
+	if (val < 0) {
+		printf("%s: Failure reading CONFIG_DONE; error=%d\n", fpga->name, val);
+	}
+	return val;
+}
+
+static inline int read_CRC_ERROR(struct cyc10_plat *fpga)
+{
+	int val = dm_gpio_get_value(&fpga->crc_error);
+	if (val < 0) {
+		printf("%s: Failure reading CRC_ERROR; error=%d\n", fpga->name, val);
+	}
+	return val;
+}
+
+/*
+ * Service routine to read status register until ready, or timeout occurs.
+ * Returns non-zero if error.
+ */
+static int cyc10_wait_until_conf_done(struct cyc10_plat *fpga)
+{
+	unsigned long timebase;
+	timebase = get_timer(0);
+
+	while (get_timer(timebase) < USER_MODE_DELAY) {
+		if (read_nSTATUS(fpga) == 0)		/* Bad configuration */
+			return -EIO;
+		if (read_CONFIG_DONE(fpga) == 1)	/* Ready */
+			return 0;
+	}
+
+	dev_err(fpga->dev, "fpga configuration timeout\n");
+	return -ETIMEDOUT;
+}
+
+
+/* FPGA Passive Serial configuration is not erasable */
+static int cyc10_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	instr->state = MTD_ERASE_DONE;
+	return 0;
+}
+
+/* FPGA Passive Serial configuration is not readable, do dummy read */
+static int cyc10_read(struct mtd_info *mtd, loff_t from, size_t len,
+			    size_t *retlen, u_char *buf)
+{
+	for (int i = 0 ; i < len ; i++)
+		buf[i] = 0xFF;
+	*retlen = len;
+	return 0;
+}
+
+static int cyc10_write(struct mtd_info *mtd, loff_t to, size_t len,
+			     size_t *retlen, const u_char *buf)
+{
+	struct udevice *dev = mtd->dev;
+	struct spi_slave *slave = dev_get_parent_priv(dev);
+	struct cyc10_plat *fpga = dev_get_plat(dev);
+	int ret;
+
+	debug("%s: Claiming SPI Bus\n", fpga->name);
+	ret = spi_claim_bus(slave);
+	if (ret) {
+		printf("Failed to claim SPI bus\n");
+		return ret;
+	}
+
+	debug("%s: Starting configuration\n", fpga->name);
+	/* The FPGA configuration start by a positive edge on nCONFIG */
+	write_nCONFIG(fpga, 0);
+	udelay(50);	// Should be low for at least 40 us according to spec.
+	debug("nSTATUS = %d\n", read_nSTATUS(fpga));
+	write_nCONFIG(fpga, 1);
+	/* The FPGA is ready when nSTATUS is high */
+	{
+		int timeout = 200;
+		while(1) {
+			if (read_nSTATUS(fpga) != 0) {
+				break;
+			}
+			udelay(10);
+			timeout -= 10;
+			if (timeout < 0) {
+				debug("nSTATUS remains low\n");
+				return -ETIMEDOUT;
+			}
+			debug("nSTATUS = %d\n", read_nSTATUS(fpga));
+		}
+	}
+
+	/* FPGA needs some additional clocks to start up, add 8 bits */
+	/* This means we read past the buffer, which is hopefully OK */
+	debug("%s: Writing %d bytes\n", fpga->name, len);
+	ret = spi_xfer(slave, (len * 8) + 8, buf, NULL,
+		       SPI_XFER_ONCE | SPI_LSB_FIRST);
+
+	debug("%s: Releasing SPI Bus\n", fpga->name);
+	spi_release_bus(slave);
+
+	debug("%s: Waiting for CONF_DONE\n", fpga->name);
+
+	ret = cyc10_wait_until_conf_done(fpga);
+	*retlen = len;
+	return ret;
+}
+
+static void cyc10_sync(struct mtd_info *mtd)
+{
+}
+
+static int cyc10_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	return 0;
+}
+
+static int cyc10_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	return 0;
+}
+
+static int pin_request(struct udevice *dev, const char *pin_name, struct gpio_desc *pin, int mode)
+{
+	int	ret;
+	debug("gpio request: %s", pin_name);
+	if (gpio_request_by_name(dev, pin_name, 0, pin, mode )) {
+		debug("FAIL");
+		ret = -EINVAL;
+	} else {
+		ret = 0;
+		debug("OK");
+		debug("%s:%s: %s:[%d]\n", __func__, pin_name, pin->dev->name, pin->offset);
+	}
+	return ret;
+}
+
+static int cyc10_probe(struct udevice *dev)
+{
+	struct spi_slave *slave = dev_get_parent_priv(dev);
+	struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
+	struct cyc10_plat *fpga = dev_get_plat(dev);
+	struct mtd_info *mtd;
+	int ret = 0;
+	const void *blob = gd->fdt_blob;
+	int node = dev_of_offset(dev);
+	/*
+	 * Use "fpga" property as device name if it exists.
+	 * Otherwise, use "fpga#" as device name # € {0..3}«»©“”nµªßðđŋħ
+	 */
+	const unsigned char *name = fdtdec_locate_byte_array(blob, node, "fpga", 4);
+	if (name == NULL) {
+		for (int i = 0; i < 3; i++) {
+			sprintf(fpga->name, "fpga%d", i);
+			mtd = get_mtd_device_nm(fpga->name);
+			if (PTR_ERR(mtd) == -ENODEV) {
+				goto allocate;
+			}
+		}
+		printf("Cannot allocate FPGA device\n");
+		return -ENODEV;
+	} else {
+		strncpy(fpga->name, (char *) name, sizeof(fpga->name));
+	}
+
+allocate:
+	fpga->spi = slave;
+	fpga->dev = dev;
+	fpga->cs  = plat->cs;
+
+	debug("%s: slave=%p, cs=%d\n", __func__, slave, fpga->cs);
+	fpga->config_size = fdtdec_get_int(blob, node, "config-size", 0);
+	debug("%s:config-size: %s:[%d]\n", __func__, fpga->name, fpga->config_size);
+
+	ret  = pin_request(dev, "nconfig-gpios",	&fpga->nconfig,   GPIOD_IS_OUT);
+	ret |= pin_request(dev, "nstat-gpios",		&fpga->nstatus,   GPIOD_IS_IN);
+	ret |= pin_request(dev, "confd-gpios",		&fpga->conf_done, GPIOD_IS_IN);
+	if (ret != 0)
+		return ret;
+	ret  = pin_request(dev, "crc-error-gpios",	&fpga->crc_error, GPIOD_IS_IN);	// May fail.
+
+	mtd = dev_get_uclass_priv(dev);
+	mtd->dev = dev;
+	mtd->name		= fpga->name;
+	mtd->type		= MTD_FPGA;
+	mtd->flags		= MTD_WRITEABLE;
+	mtd->size		= fpga->config_size;
+	mtd->writesize		= fpga->config_size;
+	mtd->writebufsize	= mtd->writesize;
+	mtd->_erase		= cyc10_erase;
+	mtd->_read		= cyc10_read;
+	mtd->_write		= cyc10_write;
+	mtd->_sync		= cyc10_sync;
+	mtd->_lock		= cyc10_lock;
+	mtd->_unlock		= cyc10_unlock;
+	mtd->numeraseregions = 0;
+	mtd->erasesize = 0x10000;
+	if (add_mtd_device(mtd)) {
+		debug("%s: registering %s as MTD failed\n", __func__, fpga->name);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static const struct udevice_id cyc10_ids[] = {
+	{ .compatible = "intel,cyclone10" },
+	{}
+};
+
+U_BOOT_DRIVER(cyc10) = {
+	.name	= "cyc10",
+	.id	= UCLASS_MTD,
+	.of_match = cyc10_ids,
+//	.priv_auto_alloc_size	= sizeof(struct cyc10_priv),
+	.plat_auto = sizeof(struct cyc10_plat),
+	.probe	= cyc10_probe,
+};
-- 
2.17.1



More information about the U-Boot mailing list