[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