This handler uses a regular SPI master and a few GPIO to program an ALTERA FPGA
in serial mode.

Signed-off-by: Juergen Beisert <j...@pengutronix.de>
---
 drivers/Kconfig              |   1 +
 drivers/Makefile             |   1 +
 drivers/fpga/Kconfig         |  10 ++
 drivers/fpga/Makefile        |   2 +
 drivers/fpga/altera_serial.c | 278 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 292 insertions(+)
 create mode 100644 drivers/fpga/Kconfig
 create mode 100644 drivers/fpga/Makefile
 create mode 100644 drivers/fpga/altera_serial.c

diff --git a/drivers/Kconfig b/drivers/Kconfig
index d34d2c7..ac3c699 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -25,5 +25,6 @@ source "drivers/gpio/Kconfig"
 source "drivers/w1/Kconfig"
 source "drivers/pinctrl/Kconfig"
 source "drivers/bus/Kconfig"
+source "drivers/fpga/Kconfig"
 
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index ba1dc6d..fa4aace 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -24,3 +24,4 @@ obj-$(CONFIG_OFTREE) += of/
 obj-$(CONFIG_W1) += w1/
 obj-y += pinctrl/
 obj-y += bus/
+obj-y += fpga/
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
new file mode 100644
index 0000000..204e852
--- /dev/null
+++ b/drivers/fpga/Kconfig
@@ -0,0 +1,10 @@
+menu "FPGA programming"
+
+config ALTERA_SERIAL
+       bool "Altera SPI programming"
+       depends on FPGAMANAGER && OFDEVICE
+       help
+         Programming an Altera FPGA via a few GPIOs for the control lines and
+         MOSI, MISO and clock from an SPI interface for the data lines
+
+endmenu
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
new file mode 100644
index 0000000..3147634
--- /dev/null
+++ b/drivers/fpga/Makefile
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_ALTERA_SERIAL) += altera_serial.o
diff --git a/drivers/fpga/altera_serial.c b/drivers/fpga/altera_serial.c
new file mode 100644
index 0000000..cf05845
--- /dev/null
+++ b/drivers/fpga/altera_serial.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2013 Juergen Beisert <ker...@pengutronix.de>, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <fpgamgr.h>
+#include <of_gpio.h>
+#include <xfuncs.h>
+#include <malloc.h>
+#include <gpio.h>
+#include <clock.h>
+#include <spi/spi.h>
+
+/*
+ * Physical requirements:
+ * - three free GPIOs for the signals nCONFIG, CONFIGURE_DONE, nSTATUS
+ * - 32 bit per word, LSB first capable SPI master (MOSI + clock)
+ *
+ * Example how to configure this driver via device tree
+ *
+ *     fpga@0 {
+ *             compatible = "altera_serial";
+ *             nstat-gpio = <&gpio4 18 0>;
+ *             confd-gpio = <&gpio4 19 0>;
+ *             nconfig-gpio = <&gpio4 20 0>;
+ *             spi-max-frequency = <10000000>;
+ *             reg = <0>;
+ *     };
+ */
+
+struct fpga_spi {
+       int nstat_gpio; /* input GPIO to read the status line */
+       int confd_gpio; /* input GPIO to read the config done line */
+       int nconfig_gpio; /* output GPIO to start the FPGA's config */
+       struct device_d *dev;
+       struct spi_device *spi;
+       bool padding_done;
+};
+
+static int altera_spi_open(struct fpga_handler *h)
+{
+       struct fpga_spi *this = (struct fpga_spi *)h->private_data;
+       struct device_d *dev = this->dev;
+       int ret;
+
+       dev_dbg(dev, "Initiating programming\n");
+
+       /* initiate an FPGA programming */
+       gpio_set_value(this->nconfig_gpio, 0);
+
+       /*
+        * after about 2 µs the FPGA must acknowledge with
+        * STATUS and CONFIG DONE lines at low level
+        */
+       ret = wait_on_timeout(2 * 1000,
+                               (gpio_get_value(this->nstat_gpio) == 0) &&
+                               (gpio_get_value(this->confd_gpio) == 0));
+
+       if (ret != 0) {
+               dev_err(dev, "FPGA does not acknowledge the programming 
initiation\n");
+               if (gpio_get_value(this->nstat_gpio))
+                       dev_err(dev, "STATUS is still high!\n");
+               if (gpio_get_value(this->confd_gpio))
+                       dev_err(dev, "CONFIG DONE is still high!\n");
+               return ret;
+       }
+
+       /* arm the FPGA to await its new firmware */
+       gpio_set_value(this->nconfig_gpio, 1);
+
+       /* once again, we might need padding the data */
+       this->padding_done = false;
+
+       /*
+        * after about 1506 µs the FPGA must acknowledge this step
+        * with the STATUS line at high level
+        */
+       ret = wait_on_timeout(1600 * 1000,
+                               gpio_get_value(this->nstat_gpio) == 1);
+       if (ret != 0) {
+               dev_err(dev, "FPGA does not acknowledge the programming 
start\n");
+               return ret;
+       }
+
+       dev_dbg(dev, "Initiating passed\n");
+       /* at the end, wait at least 2 µs prior beginning writing data */
+       ndelay(2 * 1000);
+
+       return 0;
+}
+
+static int altera_spi_write(struct fpga_handler *h, const void *buf, size_t sz)
+{
+       struct fpga_spi *this = (struct fpga_spi *)h->private_data;
+       struct device_d *dev = this->dev;
+       struct spi_transfer t[2];
+       struct spi_message m;
+       u32 dummy;
+       int ret;
+
+       spi_message_init(&m);
+
+       if (sz < sizeof(u32)) {
+               /* simple padding */
+               dummy = 0;
+               memcpy(&dummy, buf, sz);
+               buf = &dummy;
+               sz = sizeof(u32);
+               this->padding_done = true;
+       }
+
+       t[0].tx_buf = buf;
+       t[0].rx_buf = NULL;
+       t[0].len = sz;
+       spi_message_add_tail(&t[0], &m);
+
+       if (sz & 0x3) { /* padding required? */
+               u32 *word_buf = (u32 *)buf;
+               dummy = 0;
+               memcpy(&dummy, &word_buf[sz >> 2], sz & 0x3);
+               t[0].len &= ~0x03;
+               t[1].tx_buf = &dummy;
+               t[1].rx_buf = NULL;
+               t[1].len = sizeof(u32);
+               spi_message_add_tail(&t[1], &m);
+               this->padding_done = true;
+       }
+
+       ret = spi_sync(this->spi, &m);
+       if (ret != 0)
+               dev_err(dev, "programming failure\n");
+
+       return ret;
+}
+
+static int altera_spi_close(struct fpga_handler *h)
+{
+       struct fpga_spi *this = (struct fpga_spi *)h->private_data;
+       struct device_d *dev = this->dev;
+       struct spi_transfer t;
+       struct spi_message m;
+       u32 dummy = 0;
+       int ret;
+
+       dev_dbg(dev, "Finalize programming\n");
+
+       if (this->padding_done == false) {
+               spi_message_init(&m);
+               t.tx_buf = &dummy;
+               t.rx_buf = NULL;
+               t.len = sizeof(dummy);
+               spi_message_add_tail(&t, &m);
+
+               ret = spi_sync(this->spi, &m);
+               if (ret != 0)
+                       dev_err(dev, "programming failure\n");
+       }
+
+       /*
+        * when programming was successfully,
+        * both status lines should be at high level
+        */
+       ret = wait_on_timeout(10 * 1000,
+                               (gpio_get_value(this->nstat_gpio) == 1) &&
+                               (gpio_get_value(this->confd_gpio) == 1));
+       if (ret == 0) {
+               dev_dbg(dev, "Programming successfull\n");
+               return ret;
+       }
+
+       dev_err(dev, "Programming failed due to time out\n");
+       if (gpio_get_value(this->nstat_gpio) == 0)
+               dev_err(dev, "STATUS is still low!\n");
+       if (gpio_get_value(this->confd_gpio) == 0)
+               dev_err(dev, "CONFIG DONE is still low!\n");
+
+       return -EIO;
+}
+
+static int altera_spi_of(struct device_d *dev, struct fpga_spi *this)
+{
+       struct device_node *n = dev->device_node;
+
+       this->nstat_gpio = of_get_named_gpio(n, "nstat-gpio", 0);
+       if (this->nstat_gpio < 0) {
+               dev_err(dev, "Illegal value for the STATUS input GPIO\n");
+               return this->nstat_gpio;
+       }
+       this->confd_gpio = of_get_named_gpio(n, "confd-gpio", 0);
+       if (this->confd_gpio < 0) {
+               dev_err(dev, "Illegal value for the CONFIG DONE input GPIO\n");
+               return this->confd_gpio;
+       }
+       this->nconfig_gpio = of_get_named_gpio(n, "nconfig-gpio", 0);
+       if (this->nconfig_gpio < 0) {
+               dev_err(dev, "Illegal value for the CONFIGURE output GPIO\n");
+               return this->nconfig_gpio;
+       }
+
+       /* init to passive and sane values */
+       gpio_direction_output(this->nconfig_gpio, 1);
+       gpio_direction_input(this->nstat_gpio);
+       gpio_direction_input(this->confd_gpio);
+       
+       return 0;
+}
+
+static void altera_spi_init_mode(struct spi_device *spi)
+{
+       spi->bits_per_word = 32;
+       /*
+        * CPHA = CPOL = 0
+        * the FPGA expects its firmware data with LSB first
+        */
+       spi->mode = SPI_MODE_0 | SPI_LSB_FIRST;
+}
+
+static int altera_spi_probe(struct device_d *dev)
+{
+       int rc;
+       struct fpga_spi *this;
+       struct fpga_handler *h;
+       const char *alias = of_alias_get(dev->device_node);
+
+       dev_dbg(dev, "Probing FPGA firmware programmer\n");
+       h = xzalloc(sizeof(struct fpga_spi) + sizeof(struct fpga_handler));
+       this = (struct fpga_spi *)&h[1];
+       h->private_data = this;
+
+       rc = altera_spi_of(dev, this);
+       if (rc != 0)
+               return rc;
+
+       if (alias)
+               h->name = xstrdup(alias);
+       else
+               h->name = "fpga";
+
+       h->open = altera_spi_open;
+       h->write = altera_spi_write;
+       h->close = altera_spi_close;
+
+       this->spi = (struct spi_device *)dev->type_data;
+       altera_spi_init_mode(this->spi);
+       this->dev = dev;
+
+       dev_dbg(dev, "Registering FPGA firmware programmer\n");
+       rc = fpgamgr_register_handler(h);
+       if (rc != 0) {
+               free(h);
+               return rc;
+       }
+
+       return 0;
+}
+
+static struct of_device_id altera_spi_id_table[] = {
+       {
+               .compatible = "altera_serial",
+       },
+};
+
+static struct driver_d altera_spi_driver = {
+       .name = "fpga_spi",
+       .of_compatible = DRV_OF_COMPAT(altera_spi_id_table),
+       .probe = altera_spi_probe,
+};
+device_spi_driver(altera_spi_driver);
-- 
1.8.4.rc3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to