This driver is a generic interface for programming a
bitstream into a Xilinx FPGA. This driver is passed a
structure containing the GPIO pins to use (DONE, PROG
and INIT), and a write_byte() callback to program each
byte. It also supports partial reconfiguration.
Signed-off-by: Hugo Villeneuve <[EMAIL PROTECTED]>
---
drivers/misc/Kconfig | 10 +
drivers/misc/Makefile | 1 +
drivers/misc/fpgaload.c | 430 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/fpgaload.h | 50 ++++++
4 files changed, 491 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/fpgaload.c
create mode 100644 include/linux/fpgaload.h
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 4e84e90..6c2c3c8 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -500,4 +500,14 @@ config SGI_GRU_DEBUG
This option enables addition debugging code for the SGI GRU driver. If
you are unsure, say N.
+config FPGALOAD
+ tristate "FPGA bitstream loader support"
+ help
+ This option enables support for the FPGA bitstream loader.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fpgaload.
+
+ If unsure, say N.
+
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index ed7e1d0..c9bd8e8 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -32,3 +32,4 @@ obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
obj-$(CONFIG_SGI_XP) += sgi-xp/
obj-$(CONFIG_SGI_GRU) += sgi-gru/
obj-$(CONFIG_HP_ILO) += hpilo.o
+obj-$(CONFIG_FPGALOAD) += fpgaload.o
diff --git a/drivers/misc/fpgaload.c b/drivers/misc/fpgaload.c
new file mode 100644
index 0000000..d4f2408
--- /dev/null
+++ b/drivers/misc/fpgaload.c
@@ -0,0 +1,430 @@
+/*
+ * fpgaload driver
+ *
+ * Copyright (C) 2008 Lyrtech <www.lyrtech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/fpgaload.h>
+#include <linux/delay.h>
+
+#include <asm/gpio.h>
+
+#define MODULE_NAME "fpgaload"
+
+/* Define this to have verbose debug messages. */
+#define FPGALOAD_DEBUG 1
+
+#ifdef FPGALOAD_DEBUG
+#define DBGMSG(fmt, args...) \
+printk(KERN_INFO "%s: "fmt"\n" , MODULE_NAME, ## args)
+#define FAILMSG(fmt, args...) \
+printk(KERN_ERR "%s: "fmt"\n" , MODULE_NAME, ## args)
+#else
+#define DBGMSG(fmt, args...)
+#define FAILMSG(fmt, args...)
+#endif
+
+#define FPGA_WAIT_TIMEOUT 100000
+#define XFER_SIZE 100
+
+#define XC3S_WORD_SIZE 2
+#define XC4V_WORD_SIZE 4
+
+/* Normally, we should check if INIT_B is low during configuration, indicating
+ * a configuration error. But this may cause problems for bitstreams where the
+ * INIT_B pin is used as a GPIO after configuration. */
+/* #define CHECK_INIT_LOW_DURING_PROG 1 */
+
+#define BITSTREAM_SYNC_BYTE1 (0xAA)
+#define BITSTREAM_SYNC_BYTE2 (0x99)
+#define BITSTREAM_SYNC_BYTE3 (0x55)
+#define BITSTREAM_SYNC_BYTE4 (0x66)
+
+#define BITSTREAM_PACKET_HEADER_TYPE1 (1)
+#define BITSTREAM_PACKET_HEADER_TYPE2 (2)
+
+#define BITSTREAM_TYPE1_OPCODE_WRITE (2)
+
+#define BITSTREAM_TYPE1_REG_ADDR_FDRI (2)
+
+/* Structure of a TYPE1 packet. */
+struct t1_pkt_xc4v_t {
+ u32 word_count:11;
+ u32 reserved2:2;
+ u32 address:5;
+ u32 reserved1:9;
+ u32 opcode:2;
+ u32 header:3;
+};
+
+struct t1_pkt_xc3s_t {
+ u16 word_count:5;
+ u16 address:6;
+ u16 opcode:2;
+ u16 header:3; /* type */
+};
+
+/* Structure of a TYPE2 packet. */
+struct t2_pkt_xc4v_t {
+ u32 word_count:27;
+ u32 opcode:2; /* Reserved. */
+ u32 header:3;
+};
+
+struct t2_pkt_xc3s_t {
+ u16 word_count:11;
+ u16 opcode:2; /* Reserved. */
+ u16 header:3;
+};
+
+/*
+ * Toggles the CCLK line on the board-specific interface the number of times
+ * specified by <cycles>.
+ */
+static int bitstr_load_make_clock(const struct fpgaload_t *fpgaload, int
cycles)
+{
+ int retval;
+ int k;
+ u8 dummy = 0;
+
+ for (k = 0; k < cycles; k++) {
+ retval = fpgaload->write_byte(&dummy, 1);
+ if (retval < 0)
+ return retval;
+ }
+
+ return 0;
+}
+
+/* Search for bitstream sync word. */
+static int bitstr_search_sync_word(const u8 *buffer, size_t length,
+ const u8 *sync_word, ssize_t sync_word_size)
+{
+ int k;
+
+ for (k = 0; k < length; k++, buffer++) {
+ if (memcmp(buffer, sync_word, sync_word_size) == 0) {
+ DBGMSG(" Synchronization word found at offset 0x%02X.",
+ k);
+ return k;
+ }
+ }
+
+ return 0;
+}
+
+static int bitstr_get_payload_size(int fpga_family, int sws,
+ const u8 *buffer, ssize_t length)
+{
+ int index = 0;
+
+ /* Find the payload size. */
+ while (index < length) {
+ switch (fpga_family) {
+ case FPGA_FAMILY_XILINX_XC4V:
+ {
+ u32 tmp = ntohl(*((u32 *) &buffer[index]));
+ struct t1_pkt_xc4v_t *t1 =
+ (struct t1_pkt_xc4v_t *) &tmp;
+
+ /* Search for type 1 packet header. */
+ if ((t1->header == BITSTREAM_PACKET_HEADER_TYPE1) &&
+ (t1->opcode == BITSTREAM_TYPE1_OPCODE_WRITE) &&
+ (t1->address == BITSTREAM_TYPE1_REG_ADDR_FDRI)) {
+ if (t1->word_count != 0)
+ return t1->word_count;
+ else {
+ struct t2_pkt_xc4v_t *t2;
+
+ tmp = ntohl(*((u32 *)
+ &buffer[index + sws]));
+ t2 = (struct t2_pkt_xc4v_t *) &tmp;
+
+ /* Search for type 2 packet header just
+ * after type1 packet. */
+ if ((t2->header ==
+ BITSTREAM_PACKET_HEADER_TYPE2))
+ return t2->word_count;
+ }
+ }
+ }
+ break;
+ case FPGA_FAMILY_XILINX_XC3S:
+ {
+ u16 tmp = ntohs(*((u16 *) &buffer[index]));
+ struct t2_pkt_xc3s_t *t2 =
+ (struct t2_pkt_xc3s_t *) &tmp;
+
+ /* Search for type 2 packet header just after
+ * type1 packet. */
+ if ((t2->header == BITSTREAM_PACKET_HEADER_TYPE2)) {
+ DBGMSG(" Type 2 packet found at offset $%02X.",
+ index);
+ return ntohl(*((u32 *) &buffer[index + sws]));
+ }
+ /* Word-size aligned when sync word has been found. */
+ index += sws;
+ }
+ break;
+ }
+
+ /* Word-size aligned when sync word has been found. */
+ index += sws;
+ }
+
+ return 0; /* Not found */
+}
+
+/*
+ * Return value:
+ * 0: Error
+ * 1: Full bitstream
+ * 2: Partial bitstream
+ */
+static int bitstream_parse_header(const u8 *buffer, size_t length,
+ int fpga_family, size_t payload_full_size)
+{
+ int index = 0;
+ size_t payload_size = 0;
+ u8 sync_word[] = {
+ BITSTREAM_SYNC_BYTE1,
+ BITSTREAM_SYNC_BYTE2,
+ BITSTREAM_SYNC_BYTE3,
+ BITSTREAM_SYNC_BYTE4,
+ };
+ int sync_word_size; /* Size in bytes */
+
+ switch (fpga_family) {
+ case FPGA_FAMILY_XILINX_XC3S:
+ sync_word_size = XC3S_WORD_SIZE;
+ break;
+ case FPGA_FAMILY_XILINX_XC4V:
+ sync_word_size = XC4V_WORD_SIZE;
+ break;
+ default:
+ FAILMSG("Error, invalid FPGA family number.");
+ return BITSTREAM_MODE_UNKNOWN;
+ }
+
+ DBGMSG(" Sync. word size = %d", sync_word_size);
+
+ /* Search for bitstream sync word. */
+ index = bitstr_search_sync_word(buffer, length,
+ sync_word, sync_word_size);
+ if (index == 0) {
+ FAILMSG("Error: Synchronization word not found.");
+ return BITSTREAM_MODE_UNKNOWN;
+ }
+
+ /* Get payload size. */
+ payload_size = bitstr_get_payload_size(fpga_family, sync_word_size,
+ &buffer[index], length - index);
+ if (payload_size == 0) {
+ /* Warning only, assuming FULL bitstream. */
+ FAILMSG("Warning: payload size not found.");
+ return BITSTREAM_MODE_FULL;
+ }
+
+ payload_size *= sync_word_size; /* Length in bytes. */
+
+ DBGMSG(" Payload size: %d kb", payload_size / 1024);
+
+ /* Is it a full or a partial bitstream? */
+ if (payload_size == payload_full_size)
+ return BITSTREAM_MODE_FULL;
+ else
+ return BITSTREAM_MODE_PARTIAL;
+}
+
+/*
+ * Bitstreams supported: Full or Partial.
+ * Note: Full bitstream that supports partial bitstream must be generated with
+ * option Persist = true.
+ */
+int fpgaload_bitstream_load(const struct fpgaload_t *fpgaload,
+ const u8 *data, size_t size,
+ int *bitstream_mode)
+{
+ int k;
+ int retval;
+ int timeout_counter;
+
+ *bitstream_mode = bitstream_parse_header(data, size,
+ fpgaload->fpga_family,
+ fpgaload->payload_full_size);
+ switch (*bitstream_mode) {
+ case BITSTREAM_MODE_FULL:
+ DBGMSG(" Bitstream type: FULL");
+ /* Toggle PROG_B Pin and wait 300nS before proceeding. */
+ gpio_set_value(fpgaload->program_b, 0);
+ udelay(1);
+
+ /* Confirm that INIT_B is low */
+ if (gpio_get_value(fpgaload->init_b) != 0) {
+ FAILMSG("Error: INIT_B not LOW when PROG is LOW.");
+ return -EIO;
+ }
+
+ break;
+ case BITSTREAM_MODE_PARTIAL:
+ DBGMSG(" Bitstream type: PARTIAL");
+ break;
+ case BITSTREAM_MODE_UNKNOWN:
+ default:
+ DBGMSG(" Bitstream type: UNKNOWN");
+ return -EINVAL;
+ break;
+ }
+
+ /* For partial bitstream, PROGRAM_B is already high. */
+ retval = bitstr_load_make_clock(fpgaload, 3);
+ if (retval < 0)
+ return retval;
+
+ gpio_set_value(fpgaload->program_b, 1);
+
+ /* Wait for INIT_B pin to go high. */
+ timeout_counter = 0;
+ while ((gpio_get_value(fpgaload->init_b) == 0) &&
+ (timeout_counter < FPGA_WAIT_TIMEOUT)) {
+ retval = bitstr_load_make_clock(fpgaload, 3);
+ if (retval < 0)
+ return retval;
+
+ timeout_counter++;
+ }
+
+ if (timeout_counter == FPGA_WAIT_TIMEOUT) {
+ /* Timeout error. */
+ FAILMSG("Error: timeout while waiting for INIT_B to go HIGH.");
+ return -EIO;
+ }
+
+ /* Send actual bitstream data to FPGA one byte at a time. */
+ for (k = 0; k < size; k += XFER_SIZE) {
+ retval = fpgaload->write_byte((u8 *) &data[k],
+ XFER_SIZE);
+ if (retval < 0)
+ return retval;
+
+#ifdef CHECK_INIT_LOW_DURING_PROG
+ if (gpio_get_value(fpgaload->init_b) == 0) {
+ /* Error if INIT_B goes low during programming. */
+ FAILMSG("Error: INIT_B LOW during programming.");
+ return -EIO;
+ }
+#endif
+ }
+
+ /* Pulse the clock line ten times at the end. */
+ retval = bitstr_load_make_clock(fpgaload, 10);
+ if (retval < 0)
+ return retval;
+
+ /* FPGA DONE pin must go high. */
+ timeout_counter = 0;
+ while ((gpio_get_value(fpgaload->done) == 0) &&
+ (timeout_counter < FPGA_WAIT_TIMEOUT))
+ timeout_counter++;
+
+ if (gpio_get_value(fpgaload->done) == 0) {
+ /* Timeout error. */
+ FAILMSG("Error: timeout while waiting for DONE to go HIGH.");
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(fpgaload_bitstream_load);
+
+/* Release DaVinci GPIO to FPGA control pins for the Select MAP mode.
+ * NOTE: This code should be eventually be moved to the board init section. */
+static void fpgaload_release_gpio(const struct fpgaload_t *fpgaload)
+{
+ gpio_free(fpgaload->done);
+ gpio_free(fpgaload->init_b);
+ gpio_free(fpgaload->program_b);
+}
+
+int fpgaload_register(const struct fpgaload_t *fpgaload)
+{
+ int retval;
+
+ if (fpgaload->write_byte == NULL) {
+ FAILMSG("Error, fpgaload->write_byte() callback not defined.");
+ return -EINVAL;
+ }
+
+ /* Configure FPGA PROGRAM_B GPIO. */
+ retval = gpio_request(fpgaload->program_b, "fpga_program_b");
+ if (retval == 0) /* FPGA_PROGRAM_B must be initially HIGH. */
+ retval = gpio_direction_output(fpgaload->program_b, 1);
+ if (retval != 0)
+ goto gpio_error;
+
+ /* Configure FPGA INIT_B GPIO. */
+ retval = gpio_request(fpgaload->init_b, "fpga_init_b");
+ if (retval == 0)
+ retval = gpio_direction_input(fpgaload->init_b);
+ if (retval != 0)
+ goto gpio_error;
+
+ /* Configure FPGA DONE GPIO. */
+ retval = gpio_request(fpgaload->done, "fpga_done");
+ if (retval == 0)
+ retval = gpio_direction_input(fpgaload->done);
+ if (retval != 0)
+ goto gpio_error;
+
+ return 0;
+
+gpio_error:
+ fpgaload_release_gpio(fpgaload);
+ return retval;
+}
+EXPORT_SYMBOL(fpgaload_register);
+
+void fpgaload_unregister(const struct fpgaload_t *fpgaload)
+{
+ fpgaload_release_gpio(fpgaload);
+}
+EXPORT_SYMBOL(fpgaload_unregister);
+
+int __init fpgaload_init(void)
+{
+ DBGMSG("fpgaload_init()");
+
+ return 0;
+}
+module_init(fpgaload_init);
+
+void __exit fpgaload_exit(void)
+{
+ DBGMSG("fpgaload_exit()");
+}
+module_exit(fpgaload_exit);
+
+MODULE_AUTHOR("Hugo Villeneuve <[EMAIL PROTECTED]>");
+MODULE_DESCRIPTION("FPGA bitstream loader");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/fpgaload.h b/include/linux/fpgaload.h
new file mode 100644
index 0000000..e68998f
--- /dev/null
+++ b/include/linux/fpgaload.h
@@ -0,0 +1,50 @@
+/*
+ * FPGA bitstream load header file.
+ *
+ * Copyright (C) 2008 Lyrtech <www.lyrtech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef FPGALOAD_H
+#define FPGALOAD_H 1
+
+/* FPGA device-specific informations and functions. */
+struct fpgaload_t {
+ enum {
+ FPGA_FAMILY_XILINX_XC3S,
+ FPGA_FAMILY_XILINX_XC4V,
+ } fpga_family;
+ ssize_t payload_full_size;
+ u8 program_b;
+ u8 init_b;
+ u8 done;
+ int (*write_byte)(u8 *, int);
+};
+
+/* Bitstream types. */
+#define BITSTREAM_MODE_UNKNOWN 0
+#define BITSTREAM_MODE_FULL 1
+#define BITSTREAM_MODE_PARTIAL 2
+
+int fpgaload_register(const struct fpgaload_t *fpgaload);
+
+void fpgaload_unregister(const struct fpgaload_t *fpgaload);
+
+int fpgaload_bitstream_load(const struct fpgaload_t *fpgaload,
+ const u8 *data, size_t size,
+ int *bitstream_mode);;
+
+#endif /* FPGALOAD_H */
--
1.5.5
_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source