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

Reply via email to