On 15:31 Tue 12 Nov , Sascha Hauer wrote: > From: Juergen Beisert <jbe@xxxxxxxxxxxxxx> > > This handler uses a regular SPI master and a few GPIOs to program an > Altera FPGA in serial mode. > > Signed-off-by: Juergen Beisert <jbe@xxxxxxxxxxxxxx> > Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> > --- > drivers/Kconfig | 1 + > drivers/Makefile | 1 + > drivers/firmware/Kconfig | 11 ++ > drivers/firmware/Makefile | 1 + > drivers/firmware/altera_serial.c | 307 +++++++++++++++++++++++++++++++++++++++ > 5 files changed, 321 insertions(+) > create mode 100644 drivers/firmware/Kconfig > create mode 100644 drivers/firmware/Makefile > create mode 100644 drivers/firmware/altera_serial.c > > diff --git a/drivers/Kconfig b/drivers/Kconfig > index d34d2c7..71d840c 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/firmware/Kconfig" > > endmenu > diff --git a/drivers/Makefile b/drivers/Makefile > index ba1dc6d..bf03d54 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 += firmware/ > diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig > new file mode 100644 > index 0000000..28a173b > --- /dev/null > +++ b/drivers/firmware/Kconfig > @@ -0,0 +1,11 @@ > +menu "Firmware Drivers" > + > +config FIRMWARE_ALTERA_SERIAL > + bool "Altera SPI programming" > + depends on OFDEVICE > + select FIRMWARE > + 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/firmware/Makefile b/drivers/firmware/Makefile > new file mode 100644 > index 0000000..ec6a5a1 > --- /dev/null > +++ b/drivers/firmware/Makefile > @@ -0,0 +1 @@ > +obj-$(CONFIG_FIRMWARE_ALTERA_SERIAL) += altera_serial.o > diff --git a/drivers/firmware/altera_serial.c b/drivers/firmware/altera_serial.c > new file mode 100644 > index 0000000..047b6bd > --- /dev/null > +++ b/drivers/firmware/altera_serial.c > @@ -0,0 +1,307 @@ > +/* > + * Copyright (c) 2013 Juergen Beisert <kernel@xxxxxxxxxxxxxx>, 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 <firmware.h> > +#include <of_gpio.h> > +#include <xfuncs.h> > +#include <malloc.h> > +#include <gpio.h> > +#include <clock.h> > +#include <spi/spi.h> > + > +#include <fcntl.h> > +#include <fs.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>; you are missing on 's' to gpio mandatory for all the gpios binding Best Regards, J. > + * spi-max-frequency = <10000000>; > + * reg = <0>; > + * }; > + */ > + > +struct fpga_spi { > + struct firmware_handler fh; > + 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 firmware_handler *fh) > +{ > + struct fpga_spi *this = container_of(fh, struct fpga_spi, fh); > + 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); no one check the return of gpio_set but we need to on a i2c-gpio it might fail > + > + /* 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); so udelay(2); > + > + return 0; > +} > + > +static int altera_spi_write(struct firmware_handler *fh, const void *buf, size_t sz) > +{ > + struct fpga_spi *this = container_of(fh, struct fpga_spi, fh); > + struct device_d *dev = this->dev; > + struct spi_transfer t[2]; > + struct spi_message m; > + u32 dummy; > + int ret; > + > + printf("%s: %d\n", __func__, sz); > + > + 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 firmware_handler *fh) > +{ > + struct fpga_spi *this = container_of(fh, struct fpga_spi, fh); > + 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; > + const char *name; > + int ret; > + > + name = "nstat-gpio"; > + this->nstat_gpio = of_get_named_gpio(n, name, 0); > + if (this->nstat_gpio < 0) { > + ret = this->nstat_gpio; > + goto out; > + } > + > + name = "confd-gpio"; > + this->confd_gpio = of_get_named_gpio(n, name, 0); > + if (this->confd_gpio < 0) { > + ret = this->confd_gpio; > + goto out; > + } > + > + name = "nconfig-gpio"; > + this->nconfig_gpio = of_get_named_gpio(n, name, 0); > + if (this->nconfig_gpio < 0) { > + ret = this->nconfig_gpio; > + goto out; > + } > + > + /* 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); we need to check the return and request the gpio correctly > + > + return 0; > + > +out: > + dev_err(dev, "Cannot request \"%s\" gpio: %s\n", name, strerror(-ret)); > + > + return ret; > +} > + > +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 firmware_handler *fh; > + const char *alias = of_alias_get(dev->device_node); > + const char *model = NULL; > + > + dev_dbg(dev, "Probing FPGA firmware programmer\n"); > + > + this = xzalloc(sizeof(*this)); > + fh = &this->fh; > + > + rc = altera_spi_of(dev, this); > + if (rc != 0) > + goto out; > + > + if (alias) > + fh->id = xstrdup(alias); > + else > + fh->id = xstrdup("altera-fpga"); > + > + fh->open = altera_spi_open; > + fh->write = altera_spi_write; > + fh->close = altera_spi_close; > + of_property_read_string(dev->device_node, "compatible", &model); > + if (model) > + fh->model = xstrdup(model); > + fh->dev = dev; > + > + 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 = firmwaremgr_register(fh); > + if (rc != 0) { > + free(this); > + goto out; > + } > + > + return 0; > +out: > + free(fh->id); > + free(this); > + > + return rc; > +} > + > +static struct of_device_id altera_spi_id_table[] = { > + { > + .compatible = "altr,passive-serial", > + }, > +}; > + > +static struct driver_d altera_spi_driver = { > + .name = "altera-fpga", > + .of_compatible = DRV_OF_COMPAT(altera_spi_id_table), > + .probe = altera_spi_probe, > +}; > +device_spi_driver(altera_spi_driver); > -- > 1.8.4.2 > > > _______________________________________________ > barebox mailing list > barebox@xxxxxxxxxxxxxxxxxxx > http://lists.infradead.org/mailman/listinfo/barebox _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox