Hugo Villeneuve <[EMAIL PROTECTED]> writes:
> 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]>
This FPGA driver is not DaVinci specific. Therefore, the right path
to for it to be included in the kernel is for it to be discussed on
LKML.
Kevin
> ---
> 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
_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source