summaryrefslogtreecommitdiff
path: root/packages/linux/linux-davinci-2.6.28/davinci-sffsdr/0010-Add-generic-FPGA-bitstream-loader-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'packages/linux/linux-davinci-2.6.28/davinci-sffsdr/0010-Add-generic-FPGA-bitstream-loader-driver.patch')
-rw-r--r--packages/linux/linux-davinci-2.6.28/davinci-sffsdr/0010-Add-generic-FPGA-bitstream-loader-driver.patch1512
1 files changed, 1512 insertions, 0 deletions
diff --git a/packages/linux/linux-davinci-2.6.28/davinci-sffsdr/0010-Add-generic-FPGA-bitstream-loader-driver.patch b/packages/linux/linux-davinci-2.6.28/davinci-sffsdr/0010-Add-generic-FPGA-bitstream-loader-driver.patch
new file mode 100644
index 0000000000..0e034f609a
--- /dev/null
+++ b/packages/linux/linux-davinci-2.6.28/davinci-sffsdr/0010-Add-generic-FPGA-bitstream-loader-driver.patch
@@ -0,0 +1,1512 @@
+From 500b0887632165f77f2604b07df746b4a3a16d2c Mon Sep 17 00:00:00 2001
+From: Hugo Villeneuve <hugo@hugovil.com>
+Date: Fri, 6 Mar 2009 10:23:44 -0500
+Subject: [PATCH 10/12] Add generic FPGA bitstream loader driver
+
+This driver is a generic interface for programming a
+bitstream into a FPGA. It can work with serial or
+parallel programming interfaces and support multiple
+devices and partial reconfiguration. Currently Xilinx
+XC3S and XC4V FPGAs are implemented, but other
+manufacturers like Altera could be easily added.
+
+Signed-off-by: Hugo Villeneuve <hugo@hugovil.com>
+---
+ drivers/misc/Kconfig | 31 ++
+ drivers/misc/Makefile | 3 +
+ drivers/misc/fpgadl.c | 806 +++++++++++++++++++++++++++++++++++++++++++++
+ drivers/misc/fpgadl_par.c | 258 +++++++++++++++
+ drivers/misc/fpgadl_ser.c | 244 ++++++++++++++
+ include/linux/fpgadl.h | 96 ++++++
+ 6 files changed, 1438 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/misc/fpgadl.c
+ create mode 100644 drivers/misc/fpgadl_par.c
+ create mode 100644 drivers/misc/fpgadl_ser.c
+ create mode 100644 include/linux/fpgadl.h
+
+diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
+index a11e2a0..bdc0517 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -510,6 +510,37 @@ config SGI_GRU_DEBUG
+ This option enables addition debugging code for the SGI GRU driver. If
+ you are unsure, say N.
+
++config FPGADL
++ 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 fpgadl.
++
++ If unsure, say N.
++
++config FPGADL_SER
++ tristate "FPGA serial programming driver"
++ depends on SPI_MASTER && FPGADL
++ help
++ Say Y here if your FPGA bitstream is loaded using a serial
++ interface (SPI).
++
++ To compile this driver as a module, choose M here: the
++ module will be called fpgadl_ser.
++
++config FPGADL_PAR
++ tristate "FPGA parallel programming driver"
++ depends on FPGADL
++ select BITREVERSE
++ help
++ Say Y here if your FPGA bitstream is loaded using a parallel
++ interface.
++
++ To compile this driver as a module, choose M here: the
++ module will be called fpgadl_par.
++
+ source "drivers/misc/c2port/Kconfig"
+
+ endif # MISC_DEVICES
+diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
+index 78be134..4fb6dfc 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -32,5 +32,8 @@ obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
+ obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
+ obj-$(CONFIG_SGI_XP) += sgi-xp/
+ obj-$(CONFIG_SGI_GRU) += sgi-gru/
++obj-$(CONFIG_FPGADL) += fpgadl.o
++obj-$(CONFIG_FPGADL_SER) += fpgadl_ser.o
++obj-$(CONFIG_FPGADL_PAR) += fpgadl_par.o
+ obj-$(CONFIG_HP_ILO) += hpilo.o
+ obj-$(CONFIG_C2PORT) += c2port/
+diff --git a/drivers/misc/fpgadl.c b/drivers/misc/fpgadl.c
+new file mode 100644
+index 0000000..2f03d9b
+--- /dev/null
++++ b/drivers/misc/fpgadl.c
+@@ -0,0 +1,806 @@
++/*
++ * fpgadl core driver
++ *
++ * Copyright (C) 2008 Lyrtech <www.lyrtech.com>
++ *
++ * Based on code found in book "Linux Device Drivers" by
++ * Alessandro Rubini and Jonathan Corbet, published by O'Reilly & Associates.
++ *
++ * 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/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/miscdevice.h>
++#include <linux/string.h>
++#include <linux/err.h>
++#include <linux/fs.h>
++#include <linux/firmware.h>
++#include <linux/delay.h>
++#include <linux/fpgadl.h>
++
++#include <asm/gpio.h>
++#include <asm/uaccess.h>
++
++#define MODULE_NAME "fpgadl"
++
++/* Define this to enable verbose debug messages */
++#define FPGADL_DEBUG 1
++
++static const char fpgadl_driver_version[] = "v1.0";
++
++/* Module parameters */
++static unsigned int fpgadl_debug;
++EXPORT_SYMBOL_GPL(fpgadl_debug);
++module_param_named(debug, fpgadl_debug, int, 0644);
++
++#ifdef FPGADL_DEBUG
++#define INFOMSG(fmt, args...) \
++ do { \
++ printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ## args); \
++ } while (0)
++#define DBGMSG(fmt, args...) \
++ do { \
++ if (fpgadl_debug > 0) \
++ printk(KERN_DEBUG "%s: "fmt"\n", \
++ MODULE_NAME, ## args); \
++ } while (0)
++#define DBGMSG_ENTER() \
++ DBGMSG("%s() enter", __func__);
++#define DBGMSG_LEAVE() \
++ DBGMSG("%s() leave", __func__);
++#else
++#define INFOMSG(fmt, args...) do {} while (0)
++#define DBGMSG(fmt, args...) do {} while (0)
++#define DBGMSG_ENTER() do {} while (0)
++#define DBGMSG_LEAVE() do {} while (0)
++#endif
++
++#define FAILMSG(fmt, args...) \
++ do { \
++ printk(KERN_ERR "%s: "fmt"\n", MODULE_NAME, ## args); \
++ } while (0)
++
++#define FPGA_WAIT_TIMEOUT 100000
++#define XFER_SIZE 100 /* Transfer size when writing bytes to
++ * device. */
++
++#define BITSTREAM_MAX_SIZE_OVERHEAD 10240
++
++#define XC3S_WORD_SIZE 2
++#define XC4V_WORD_SIZE 4
++
++#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;
++};
++
++#define MAX_FPGADL_DEV 4
++
++static int fpgadl_dev_count;
++static struct fpgadl_device *fpgadl_dev_array[MAX_FPGADL_DEV];
++
++int fpgadl_is_bitstream_loaded(const char *name)
++{
++ int k;
++ struct fpgadl_device *fpgadl_dev;
++
++ DBGMSG_ENTER();
++
++ for (k = 0; k < MAX_FPGADL_DEV; k++) {
++ fpgadl_dev = fpgadl_dev_array[k];
++ if (fpgadl_dev)
++ if (strncmp(fpgadl_dev->name, name, strlen(name)) == 0)
++ return fpgadl_dev->bitstream_loaded;
++ }
++
++ FAILMSG(" Device <%s> not found", name);
++ return -ENOMEM;
++}
++EXPORT_SYMBOL(fpgadl_is_bitstream_loaded);
++
++/* Respond to hotplug events. */
++static int fpgadl_uevent(struct device *dev, struct kobj_uevent_env *env)
++{
++ DBGMSG_ENTER();
++
++ if (add_uevent_var(env, "FPGADL_BUS_VERSION=%s", fpgadl_driver_version))
++ return -ENOMEM;
++ return 0;
++};
++
++/*
++ * Toggles the CCLK line on the board-specific interface the number of times
++ * specified by <cycles>.
++ */
++static int bitstr_load_make_clock(struct fpgadl_device *fpgadl_dev,
++ int cycles)
++{
++ int retval;
++ int k;
++ u8 dummy = 0;
++
++ for (k = 0; k < cycles; k++) {
++ retval = fpgadl_dev->write_byte(fpgadl_dev, &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;
++ }
++
++ /* 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);
++ payload_size *= sync_word_size; /* Length in bytes. */
++
++ if (payload_size == 0) {
++ /* Warning only, assuming FULL bitstream. */
++ DBGMSG(" Warning: payload size not found");
++ return BITSTREAM_MODE_FULL;
++ } else {
++ 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" in ISE.
++ */
++static int fpgadl_bitstream_load(struct fpgadl_device *fpgadl_dev,
++ const u8 *data, size_t size)
++{
++ int k;
++ int retval;
++ int timeout_counter;
++
++ fpgadl_dev->bitstream_loaded = 0;
++
++ fpgadl_dev->bitstream_mode =
++ bitstream_parse_header(data, size,
++ fpgadl_dev->pdata->fpga_family,
++ fpgadl_dev->pdata->payload_full_size);
++ switch (fpgadl_dev->bitstream_mode) {
++ case BITSTREAM_MODE_FULL:
++ DBGMSG(" Bitstream type: FULL");
++ /* Toggle PROG_B Pin and wait 300nS before proceeding. */
++ gpio_set_value(fpgadl_dev->pdata->program_b, 0);
++ udelay(1);
++
++ /* Confirm that INIT_B is low */
++ if (gpio_get_value(fpgadl_dev->pdata->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:
++ FAILMSG(" Bitstream type: UNKNOWN");
++ return -EINVAL;
++ break;
++ }
++
++ /* For partial bitstream, PROGRAM_B is already high. */
++ retval = bitstr_load_make_clock(fpgadl_dev, 3);
++ if (retval < 0)
++ return retval;
++
++ gpio_set_value(fpgadl_dev->pdata->program_b, 1);
++
++ /* Wait for INIT_B pin to go high. */
++ timeout_counter = 0;
++ while ((gpio_get_value(fpgadl_dev->pdata->init_b) == 0) &&
++ (timeout_counter < FPGA_WAIT_TIMEOUT)) {
++ retval = bitstr_load_make_clock(fpgadl_dev, 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 = fpgadl_dev->write_byte(fpgadl_dev,
++ (u8 *) &data[k], XFER_SIZE);
++ if (retval < 0)
++ return retval;
++
++ if (fpgadl_dev->pdata->check_init_low) {
++ if (gpio_get_value(fpgadl_dev->pdata->init_b) == 0) {
++ /* Error if INIT_B goes low here. */
++ FAILMSG("Error: INIT_B LOW during programming");
++ return -EIO;
++ }
++ }
++ }
++
++ /* Pulse the clock line ten times at the end. */
++ retval = bitstr_load_make_clock(fpgadl_dev, 10);
++ if (retval < 0)
++ return retval;
++
++ /* FPGA DONE pin must go high. */
++ timeout_counter = 0;
++ while ((gpio_get_value(fpgadl_dev->pdata->done) == 0) &&
++ (timeout_counter < FPGA_WAIT_TIMEOUT))
++ timeout_counter++;
++
++ if (gpio_get_value(fpgadl_dev->pdata->done) == 0) {
++ /* Timeout error. */
++ FAILMSG("Error: timeout while waiting for DONE to go HIGH");
++ return -EIO;
++ }
++
++ INFOMSG("Bitstream loaded");
++ fpgadl_dev->bitstream_loaded = 1;
++
++ return 0;
++}
++
++/* Open method. */
++static int fpgadl_open(struct inode *inode, struct file *filp)
++{
++ int k;
++ int found = 0;
++ struct fpgadl_device *fpgadl_dev;
++
++ DBGMSG_ENTER();
++ DBGMSG(" Opening device minor %d", MINOR(inode->i_rdev));
++
++ for (k = 0; k < fpgadl_dev_count; k++) {
++ fpgadl_dev = fpgadl_dev_array[k];
++ if (fpgadl_dev) {
++ if (fpgadl_dev->miscdev.minor == MINOR(inode->i_rdev)) {
++ found = 1;
++ break;
++ }
++ }
++ }
++
++ if (!found) {
++ FAILMSG(" Invalid minor device");
++ return -ENOMEM;
++ }
++
++ filp->private_data = fpgadl_dev;
++
++ fpgadl_dev->bitstream_length = 0;
++ fpgadl_dev->bitstream_data = kmalloc(fpgadl_dev->bitstream_max_size,
++ GFP_KERNEL);
++ if (!fpgadl_dev->bitstream_data) {
++ FAILMSG("Failed to allocate memory for bitstream");
++ return -ENOMEM;
++ }
++
++ fpgadl_dev->bitstream_buffer_allocated = 1;
++
++ return 0;
++}
++
++/* Write method. Fill buffer with bitstream data. */
++static ssize_t fpgadl_write(struct file *filp, const char __user *buff,
++ size_t count, loff_t *offp)
++{
++ struct fpgadl_device *fpgadl_dev = filp->private_data;
++
++ if ((fpgadl_dev->bitstream_length + count) >=
++ fpgadl_dev->bitstream_max_size) {
++ FAILMSG("Bitstream buffer size exceeded");
++ return -EFBIG;
++ }
++
++ if (copy_from_user(fpgadl_dev->bitstream_data +
++ fpgadl_dev->bitstream_length,
++ (void __user *) buff, count))
++ return -EFAULT;
++
++ fpgadl_dev->bitstream_length += count;
++
++ return count;
++}
++
++/* Release method. This will initiate the FPGA programming. */
++static int fpgadl_release(struct inode *inode, struct file *filp)
++{
++ int retval;
++ struct fpgadl_device *fpgadl_dev = filp->private_data;
++
++ if (!fpgadl_dev->bitstream_data)
++ return -EFAULT;
++
++ retval = fpgadl_bitstream_load(fpgadl_dev,
++ fpgadl_dev->bitstream_data,
++ fpgadl_dev->bitstream_length);
++ kfree(fpgadl_dev->bitstream_data);
++ fpgadl_dev->bitstream_buffer_allocated = 0;
++
++ return retval;
++}
++
++static struct file_operations fops_fpgadl = {
++ .owner = THIS_MODULE,
++ .open = fpgadl_open,
++ .write = fpgadl_write,
++ .release = fpgadl_release
++};
++
++/* Match fpgadl devices to drivers. Just do a simple name test. */
++static int fpgadl_device_match(struct device *dev,
++ struct device_driver *drv)
++{
++ DBGMSG_ENTER();
++ return !strncmp(dev->bus_id, drv->name, strlen(drv->name));
++}
++
++static ssize_t show_version(struct device_driver *driver, char *buf)
++{
++ struct fpgadl_driver *fpgadldriver = to_fpgadl_driver(driver);
++
++ sprintf(buf, "%s\n", fpgadldriver->version);
++ return strlen(buf);
++}
++
++int fpgadl_register_driver(struct fpgadl_driver *drv)
++{
++ int res;
++
++ DBGMSG_ENTER();
++
++ /* Initialize common driver fields */
++ drv->driver.bus = &fpgadl_bus_type;
++
++ /* Register with core */
++ res = driver_register(&drv->driver);
++ if (res)
++ FAILMSG(" driver_register() failed");
++
++ drv->version_attr.attr.name = "version";
++ drv->version_attr.attr.owner = drv->module;
++ drv->version_attr.attr.mode = S_IRUGO;
++ drv->version_attr.show = show_version;
++ drv->version_attr.store = NULL;
++ res = driver_create_file(&drv->driver, &drv->version_attr);
++
++ return res;
++}
++EXPORT_SYMBOL(fpgadl_register_driver);
++
++void fpgadl_unregister_driver(struct fpgadl_driver *drv)
++{
++ DBGMSG_ENTER();
++ driver_unregister(&drv->driver);
++}
++EXPORT_SYMBOL(fpgadl_unregister_driver);
++
++/* The fpgadl bus device. */
++static void fpgadl_bus_release(struct device *dev)
++{
++ DBGMSG_ENTER();
++}
++
++struct device fpgadl_bus = {
++ .bus_id = "fpgadl0",
++ .release = fpgadl_bus_release
++};
++
++struct bus_type fpgadl_bus_type = {
++ .name = "fpgadl",
++ .match = fpgadl_device_match,
++ .uevent = fpgadl_uevent,
++};
++EXPORT_SYMBOL(fpgadl_bus_type);
++
++/* Export a simple sysfs attribute. */
++static ssize_t show_bus_version(struct bus_type *bus, char *buf)
++{
++ return snprintf(buf, PAGE_SIZE, "%s\n", fpgadl_driver_version);
++}
++
++static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
++
++/*
++ * fpgadl devices.
++ * For now, no references to fpgadlbus devices go out which are not
++ * tracked via the module reference count, so we use a no-op
++ * release function.
++ */
++static void fpgadl_dev_release(struct device *dev)
++{
++ DBGMSG_ENTER();
++}
++
++/* Release DaVinci GPIO to FPGA control pins. */
++static void fpgadl_release_gpio(struct fpgadl_pdata_t *pdata)
++{
++ gpio_free(pdata->done);
++ gpio_free(pdata->init_b);
++ gpio_free(pdata->program_b);
++}
++
++static int fpgadl_setup_gpio(struct fpgadl_pdata_t *pdata)
++{
++ int retval;
++
++ /* Configure FPGA PROGRAM_B GPIO. */
++ retval = gpio_request(pdata->program_b, "fpga_program_b");
++ if (retval == 0) /* FPGA_PROGRAM_B must be initially HIGH. */
++ retval = gpio_direction_output(pdata->program_b, 1);
++ if (retval != 0)
++ goto gpio_error;
++
++ /* Configure FPGA INIT_B GPIO. */
++ retval = gpio_request(pdata->init_b, "fpga_init_b");
++ if (retval == 0)
++ retval = gpio_direction_input(pdata->init_b);
++ if (retval != 0)
++ goto gpio_error;
++
++ /* Configure FPGA DONE GPIO. */
++ retval = gpio_request(pdata->done, "fpga_done");
++ if (retval == 0)
++ retval = gpio_direction_input(pdata->done);
++ if (retval != 0)
++ goto gpio_error;
++
++ return 0;
++
++gpio_error:
++ fpgadl_release_gpio(pdata);
++ return retval;
++}
++
++static void fpgadl_cleanup(struct fpgadl_device *fpgadl_dev)
++{
++ DBGMSG_ENTER();
++
++ if (!fpgadl_dev)
++ return;
++
++ fpgadl_dev_array[fpgadl_dev->id] = NULL;
++
++ /* Get rid of any allocated buffer, not freed */
++ if (fpgadl_dev->bitstream_buffer_allocated)
++ kfree(fpgadl_dev->bitstream_data);
++
++ switch (fpgadl_dev->state) {
++ case FPGADL_DEV_STATE_CHAR_DEV_REGISTERED:
++ misc_deregister(&fpgadl_dev->miscdev);
++ case FPGADL_DEV_STATE_GPIO_REGISTERED:
++ fpgadl_release_gpio(fpgadl_dev->pdata);
++ case FPGADL_DEV_STATE_DEVICE_REGISTERED:
++ device_unregister(&fpgadl_dev->dev);
++ case FPGADL_DEV_STATE_START:
++ break;
++ }
++}
++
++int fpgadl_register_device(struct fpgadl_device *fpgadl_dev)
++{
++ int res;
++ const struct firmware *fw_entry;
++
++ DBGMSG_ENTER();
++
++ fpgadl_dev->state = FPGADL_DEV_STATE_START;
++
++ /* Sanity checks. */
++ if (!fpgadl_dev->name) {
++ FAILMSG(" Error, missing device name");
++ res = -EFAULT;
++ goto error;
++ }
++
++ if (!fpgadl_dev->write_byte) {
++ FAILMSG(" Error, missing write_byte() callback");
++ res = -ENOMEM;
++ goto error;
++ }
++
++ if (fpgadl_dev_count == MAX_FPGADL_DEV) {
++ FAILMSG("Maximum number of devices reached (%d)",
++ fpgadl_dev_count);
++ res = -ENODEV;
++ goto error;
++ }
++
++ DBGMSG(" device %d", fpgadl_dev_count);
++
++ /* Set some default values. */
++ fpgadl_dev->bitstream_loaded = 0;
++ fpgadl_dev->bitstream_buffer_allocated = 0;
++ fpgadl_dev->bitstream_max_size =
++ fpgadl_dev->pdata->payload_full_size +
++ BITSTREAM_MAX_SIZE_OVERHEAD;
++
++ fpgadl_dev->dev.bus = &fpgadl_bus_type;
++ fpgadl_dev->dev.parent = &fpgadl_bus;
++ fpgadl_dev->dev.release = fpgadl_dev_release;
++ strncpy(fpgadl_dev->dev.bus_id, fpgadl_dev->name, BUS_ID_SIZE);
++ res = device_register(&fpgadl_dev->dev);
++ if (res) {
++ FAILMSG(" device_register() failed");
++ goto error;
++ }
++ fpgadl_dev->state = FPGADL_DEV_STATE_DEVICE_REGISTERED;
++
++ res = fpgadl_setup_gpio(fpgadl_dev->pdata);
++ if (res < 0) {
++ FAILMSG("Error registering GPIOs");
++ goto error;
++ }
++ fpgadl_dev->state = FPGADL_DEV_STATE_GPIO_REGISTERED;
++
++ fpgadl_dev->miscdev.name = fpgadl_dev->name;
++ fpgadl_dev->miscdev.minor = MISC_DYNAMIC_MINOR;
++ fpgadl_dev->miscdev.fops = &fops_fpgadl;
++ res = misc_register(&fpgadl_dev->miscdev);
++ if (res < 0) {
++ FAILMSG("Error registering misc driver");
++ goto error;
++ }
++ DBGMSG(" MINOR = %d", fpgadl_dev->miscdev.minor);
++ fpgadl_dev->state = FPGADL_DEV_STATE_CHAR_DEV_REGISTERED;
++
++ /* Try to load firmware through hotplug if available. */
++ res = request_firmware(&fw_entry, fpgadl_dev->pdata->bitstream_name,
++ &fpgadl_dev->dev);
++ if (res < 0) {
++ /* Not an error preventing the driver from being loaded. */
++ res = 0;
++ DBGMSG("Info: firmware not available");
++ } else {
++ res = fpgadl_bitstream_load(fpgadl_dev, fw_entry->data,
++ fw_entry->size);
++ release_firmware(fw_entry);
++ }
++
++ fpgadl_dev->id = fpgadl_dev_count;
++ fpgadl_dev_array[fpgadl_dev_count] = fpgadl_dev;
++ fpgadl_dev_count++;
++
++ return 0;
++
++error:
++ fpgadl_cleanup(fpgadl_dev);
++ return res;
++}
++EXPORT_SYMBOL(fpgadl_register_device);
++
++void fpgadl_unregister_device(struct fpgadl_device *fpgadl_dev)
++{
++ DBGMSG_ENTER();
++ fpgadl_cleanup(fpgadl_dev);
++}
++EXPORT_SYMBOL(fpgadl_unregister_device);
++
++static int __init fpgadl_init(void)
++{
++ int res;
++
++ DBGMSG_ENTER();
++ INFOMSG("FPGA bitstream loader %s", fpgadl_driver_version);
++
++ res = bus_register(&fpgadl_bus_type);
++ if (res) {
++ FAILMSG(" bus_register() failed");
++ goto fail_bus;
++ }
++
++ if (bus_create_file(&fpgadl_bus_type, &bus_attr_version)) {
++ FAILMSG("Unable to create version attribute");
++ goto fail_create_file;
++ }
++
++ res = device_register(&fpgadl_bus);
++ if (res) {
++ FAILMSG(" failed registering %s", fpgadl_bus.bus_id);
++ goto fail_dev_reg;
++ }
++
++ return 0;
++
++fail_dev_reg:
++fail_create_file:
++ bus_unregister(&fpgadl_bus_type);
++fail_bus:
++ return res;
++}
++module_init(fpgadl_init);
++
++static void __exit fpgadl_exit(void)
++{
++ DBGMSG_ENTER();
++ device_unregister(&fpgadl_bus);
++ bus_unregister(&fpgadl_bus_type);
++}
++module_exit(fpgadl_exit);
++
++MODULE_AUTHOR("Hugo Villeneuve <hvilleneuve@lyrtech.com>");
++MODULE_DESCRIPTION("FPGA bitstream loader");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/misc/fpgadl_par.c b/drivers/misc/fpgadl_par.c
+new file mode 100644
+index 0000000..66f8eba
+--- /dev/null
++++ b/drivers/misc/fpgadl_par.c
+@@ -0,0 +1,258 @@
++/*
++ * fpgadl_par.c - FPGA parallel programming 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/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/string.h>
++#include <linux/err.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/bitrev.h>
++#include <linux/fpgadl.h>
++
++#include <asm/gpio.h> /* For ioremap() */
++
++#define MODULE_NAME "fpgadl_par"
++#define MODULE_VERSION_STR "v1.0"
++
++/* Define this to enable verbose debug messages */
++#define FPGADL_PAR_DEBUG 1
++
++/* Module parameters */
++static unsigned int fpgadl_par_debug;
++EXPORT_SYMBOL_GPL(fpgadl_par_debug);
++module_param_named(debug, fpgadl_par_debug, int, 0644);
++
++#ifdef FPGADL_PAR_DEBUG
++#define INFOMSG(fmt, args...) \
++do { \
++ printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ## args); } while (0)
++#define DBGMSG(fmt, args...) \
++do { if (fpgadl_par_debug > 0) \
++ printk(KERN_DEBUG "%s: "fmt"\n", MODULE_NAME, ## args); } while (0)
++#define DBGMSG_ENTER() \
++ DBGMSG("%s() enter", __func__);
++#define DBGMSG_LEAVE() \
++ DBGMSG("%s() leave", __func__);
++#else
++#define INFOMSG(fmt, args...) do {} while (0)
++#define DBGMSG(fmt, args...) do {} while (0)
++#define DBGMSG_ENTER() do {} while (0)
++#define DBGMSG_LEAVE() do {} while (0)
++#endif
++
++#define FAILMSG(fmt, args...) \
++do { \
++ printk(KERN_ERR "%s: "fmt"\n", MODULE_NAME, ## args); } while (0)
++
++struct fpgadl_par_dev_t {
++ char devname[32];
++ enum {
++ FPGADL_PAR_DEV_STATE_STRUCT_ALLOCATED,
++ FPGADL_PAR_DEV_STATE_HAVE_IOREMAP,
++ FPGADL_PAR_DEV_STATE_FPGADL_DEV_REGISTERED,
++ } state;
++ u8 *selectmap;
++ struct fpgadl_device fpgadl_dev;
++};
++
++#define MAX_FPGADL_PAR_DEV 5
++
++static int fpgadl_par_dev_count;
++
++/*
++ * Writes a byte of data to the FPGA using the SelectMAP
++ * interface. The FPGA_SELECT_MAP_REG address is within
++ * the FPGA address space (CS3), and when we write a byte
++ * to that address, the CCLK line will be toggled.
++ */
++static int selectmap_write_byte(struct fpgadl_device *fpgadl_dev,
++ u8 *data, int size)
++{
++ int k;
++ struct fpgadl_par_dev_t *fpgadl_par_dev;
++
++ fpgadl_par_dev = (struct fpgadl_par_dev_t *) fpgadl_dev->devdata;
++
++ for (k = 0; k < size; k++)
++ fpgadl_par_dev->selectmap[0] = bitrev8(data[k]);
++
++ return 0;
++}
++
++static void fpgadl_par_cleanup(struct fpgadl_par_dev_t *dev)
++{
++ DBGMSG("fpgadl_par_cleanup");
++
++ if (!dev)
++ return;
++
++ switch (dev->state) {
++ case FPGADL_PAR_DEV_STATE_FPGADL_DEV_REGISTERED:
++ fpgadl_unregister_device(&dev->fpgadl_dev);
++ case FPGADL_PAR_DEV_STATE_HAVE_IOREMAP:
++ iounmap(dev->selectmap);
++ case FPGADL_PAR_DEV_STATE_STRUCT_ALLOCATED:
++ kfree(dev);
++ break;
++ }
++}
++
++static int __devinit fpgadl_par_probe(struct platform_device *pdev)
++{
++ int len;
++ int res;
++ struct fpgadl_par_dev_t *dev = NULL;
++ const struct resource *selectmap_res;
++
++ DBGMSG("fpgadl_par_probe()");
++
++ if (fpgadl_par_dev_count == MAX_FPGADL_PAR_DEV) {
++ FAILMSG("Maximum number of devices reached (%d)",
++ fpgadl_par_dev_count);
++ res = -ENODEV;
++ goto error;
++ }
++
++ DBGMSG(" device %d", fpgadl_par_dev_count);
++
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++ if (!dev) {
++ FAILMSG("Failed to allocate device structure");
++ res = -ENOMEM;
++ goto error;
++ }
++ /* Set some default values. */
++ dev->state = FPGADL_PAR_DEV_STATE_STRUCT_ALLOCATED;
++
++ if (!pdev->dev.platform_data) {
++ FAILMSG("Error getting platform data");
++ res = -ENODEV;
++ goto error;
++ }
++ dev->fpgadl_dev.pdata = pdev->dev.platform_data;
++ pdev->dev.driver_data = dev; /* Private driver data */
++
++ /* Assign virtual addresses to SELECTMAP I/O memory regions. */
++ selectmap_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++ "selectmap");
++ if (!selectmap_res) {
++ FAILMSG("Error getting selectmap ressource");
++ res = -ENODEV;
++ goto error;
++ }
++ len = selectmap_res->end - selectmap_res->start;
++ dev->selectmap = ioremap(selectmap_res->start, len);
++ if (!dev->selectmap) {
++ FAILMSG("Can't remap selectmap register");
++ res = -ENXIO;
++ goto error;
++ }
++ dev->state = FPGADL_PAR_DEV_STATE_HAVE_IOREMAP;
++
++ dev->fpgadl_dev.write_byte = selectmap_write_byte;
++ sprintf(dev->devname, "fpgadl_par%d", fpgadl_par_dev_count);
++ DBGMSG(" NAME = %s", dev->devname);
++ dev->fpgadl_dev.name = dev->devname;
++ dev->fpgadl_dev.devdata = dev; /* For our write_byte() callback */
++ res = fpgadl_register_device(&dev->fpgadl_dev);
++ if (res < 0) {
++ FAILMSG("Error registering fpgadl_par device");
++ goto error;
++ }
++ dev->state = FPGADL_PAR_DEV_STATE_FPGADL_DEV_REGISTERED;
++
++ fpgadl_par_dev_count++;
++
++ return 0;
++
++error:
++ fpgadl_par_cleanup(dev);
++ return res;
++}
++
++static int __devexit fpgadl_par_remove(struct platform_device *pdev)
++{
++ struct fpgadl_par_dev_t *dev = platform_get_drvdata(pdev);
++
++ DBGMSG("fpgadl_par_remove()");
++
++ fpgadl_par_cleanup(dev);
++
++ return 0;
++}
++
++static struct fpgadl_driver fpgadl_par_driver = {
++ .version = MODULE_VERSION_STR,
++ .module = THIS_MODULE,
++ .driver = {
++ .name = "fpgadl_par",
++ },
++};
++
++static struct platform_driver fpgadl_platform_driver = {
++ .driver = {
++ .name = MODULE_NAME,
++ .owner = THIS_MODULE,
++ },
++ .remove = fpgadl_par_remove,
++};
++
++static int __init fpgadl_par_init(void)
++{
++ int res;
++
++ DBGMSG("fpgadl_par_init()");
++
++ /* Register with the driver core. */
++ res = fpgadl_register_driver(&fpgadl_par_driver);
++ if (res) {
++ FAILMSG("Can't register fpgadl parallel driver");
++ return res;
++ }
++
++ /* The probe function will be called for each platform device declared
++ * in board setup code. */
++ res = platform_driver_probe(&fpgadl_platform_driver,
++ fpgadl_par_probe);
++ if (res) {
++ FAILMSG("platform_driver_probe() failed");
++ return res;
++ }
++
++ return 0;
++}
++module_init(fpgadl_par_init);
++
++static void __exit fpgadl_par_exit(void)
++{
++ DBGMSG("fpgadl_par_exit()");
++ platform_driver_unregister(&fpgadl_platform_driver);
++ fpgadl_unregister_driver(&fpgadl_par_driver);
++}
++module_exit(fpgadl_par_exit);
++
++MODULE_AUTHOR("Hugo Villeneuve <hvilleneuve@lyrtech.com>");
++MODULE_DESCRIPTION("FPGA parallel programming driver");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/misc/fpgadl_ser.c b/drivers/misc/fpgadl_ser.c
+new file mode 100644
+index 0000000..01ca5e0
+--- /dev/null
++++ b/drivers/misc/fpgadl_ser.c
+@@ -0,0 +1,244 @@
++/*
++ * fpgadl_ser.c - FPGA serial programming driver
++ *
++ * Copyright (C) 2008 Lyrtech <www.lyrtech.com>
++ *
++ * Based on SH SCI SPI interface
++ * Copyright (c) 2008 Magnus Damm
++ *
++ * 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/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/spi/spi.h>
++#include <linux/fpgadl.h>
++
++#define MODULE_NAME "fpgadl_ser"
++#define MODULE_VERSION_STR "v1.0"
++
++/* Define this to enable verbose debug messages */
++#define FPGADL_SER_DEBUG 1
++
++/* Module parameters */
++static unsigned int fpgadl_ser_debug;
++EXPORT_SYMBOL_GPL(fpgadl_ser_debug);
++module_param_named(debug, fpgadl_ser_debug, int, 0644);
++
++#ifdef FPGADL_SER_DEBUG
++#define INFOMSG(fmt, args...) \
++do { \
++ printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ## args); } while (0)
++#define DBGMSG(fmt, args...) \
++do { if (fpgadl_ser_debug > 0) \
++ printk(KERN_DEBUG "%s: "fmt"\n", MODULE_NAME, ## args); } while (0)
++#define DBGMSG_ENTER() \
++ DBGMSG("%s() enter", __func__);
++#define DBGMSG_LEAVE() \
++ DBGMSG("%s() leave", __func__);
++#else
++#define INFOMSG(fmt, args...) do {} while (0)
++#define DBGMSG(fmt, args...) do {} while (0)
++#define DBGMSG_ENTER() do {} while (0)
++#define DBGMSG_LEAVE() do {} while (0)
++#endif
++
++#define FAILMSG(fmt, args...) \
++do { \
++ printk(KERN_ERR "%s: "fmt"\n", MODULE_NAME, ## args); } while (0)
++
++struct fpgadl_ser_dev_t {
++ char devname[32];
++ enum {
++ FPGADL_SER_DEV_STATE_STRUCT_ALLOCATED,
++ FPGADL_SER_DEV_STATE_SPI_SETUP,
++ FPGADL_SER_DEV_STATE_FPGADL_DEV_REGISTERED,
++ } state;
++ struct spi_transfer t;
++ struct spi_message m;
++ struct spi_device *spi;
++ struct fpgadl_device fpgadl_dev;
++};
++
++#define MAX_FPGADL_SER_DEV 5
++
++static int fpgadl_ser_dev_count;
++
++static int fpgadl_ser_write_byte(struct fpgadl_device *fpgadl_dev,
++ u8 *data, int size)
++{
++ int status;
++ struct fpgadl_ser_dev_t *fpgadl_ser_dev;
++
++ fpgadl_ser_dev = (struct fpgadl_ser_dev_t *) fpgadl_dev->devdata;
++
++ if (!data) {
++ FAILMSG("NULL data pointer");
++ return -EFAULT;
++ }
++
++ spi_message_init(&fpgadl_ser_dev->m);
++ fpgadl_ser_dev->t.tx_buf = data;
++ fpgadl_ser_dev->t.len = size;
++ spi_message_add_tail(&fpgadl_ser_dev->t, &fpgadl_ser_dev->m);
++
++ status = spi_sync(fpgadl_ser_dev->spi, &fpgadl_ser_dev->m);
++ if (status < 0)
++ FAILMSG("spi_sync() failed (%d)", status);
++
++ return status;
++}
++
++static void fpgadl_ser_cleanup(struct fpgadl_ser_dev_t *dev)
++{
++ DBGMSG_ENTER();
++
++ if (!dev)
++ return;
++
++ switch (dev->state) {
++ case FPGADL_SER_DEV_STATE_FPGADL_DEV_REGISTERED:
++ fpgadl_unregister_device(&dev->fpgadl_dev);
++ case FPGADL_SER_DEV_STATE_SPI_SETUP:
++ case FPGADL_SER_DEV_STATE_STRUCT_ALLOCATED:
++ kfree(dev);
++ break;
++ }
++}
++
++static int __devinit fpgadl_ser_probe(struct spi_device *spi)
++{
++ int res;
++ struct fpgadl_ser_dev_t *dev = NULL;
++
++ DBGMSG_ENTER();
++
++ if (fpgadl_ser_dev_count == MAX_FPGADL_SER_DEV) {
++ FAILMSG("Maximum number of devices reached (%d)",
++ fpgadl_ser_dev_count);
++ res = -ENODEV;
++ goto error;
++ }
++
++ DBGMSG(" device %d", fpgadl_ser_dev_count);
++
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++ if (!dev) {
++ FAILMSG("Failed to allocate device structure");
++ res = -ENOMEM;
++ goto error;
++ }
++ /* Set some default values. */
++ dev->state = FPGADL_SER_DEV_STATE_STRUCT_ALLOCATED;
++
++ DBGMSG(" SPI mode = %d", spi->mode);
++
++ if (!spi->dev.platform_data) {
++ FAILMSG("Error getting platform data");
++ res = -ENODEV;
++ goto error;
++ }
++ dev->fpgadl_dev.pdata = spi->dev.platform_data;
++ spi->dev.driver_data = dev; /* Private driver data */
++
++ spi->bits_per_word = 8; /* Size of Tx and Rx transfers. */
++ res = spi_setup(spi);
++ if (res < 0) {
++ FAILMSG("Error setting-up SPI");
++ goto error;
++ }
++ dev->spi = spi;
++ dev->state = FPGADL_SER_DEV_STATE_SPI_SETUP;
++
++ dev->fpgadl_dev.write_byte = fpgadl_ser_write_byte;
++ sprintf(dev->devname, "fpgadl_ser%d", fpgadl_ser_dev_count);
++ DBGMSG(" NAME = %s", dev->devname);
++ dev->fpgadl_dev.name = dev->devname;
++ dev->fpgadl_dev.devdata = dev; /* For our write_byte() callback */
++ res = fpgadl_register_device(&dev->fpgadl_dev);
++ if (res < 0) {
++ FAILMSG("Error registering fpgadl_ser device");
++ goto error;
++ }
++ dev->state = FPGADL_SER_DEV_STATE_FPGADL_DEV_REGISTERED;
++
++ fpgadl_ser_dev_count++;
++
++ return 0;
++
++error:
++ fpgadl_ser_cleanup(dev);
++ return res;
++}
++
++static int __devexit fpgadl_ser_remove(struct spi_device *spi)
++{
++ struct fpgadl_ser_dev_t *dev = spi_get_drvdata(spi);
++
++ DBGMSG_ENTER();
++ fpgadl_ser_cleanup(dev);
++ return 0;
++}
++
++static struct spi_driver fpgadl_ser_spi_driver = {
++ .driver = {
++ .name = MODULE_NAME,
++ .bus = &spi_bus_type,
++ .owner = THIS_MODULE,
++ },
++ .probe = fpgadl_ser_probe,
++ .remove = fpgadl_ser_remove,
++};
++
++static struct fpgadl_driver fpgadl_ser_driver = {
++ .version = MODULE_VERSION_STR,
++ .module = THIS_MODULE,
++ .driver = {
++ .name = "fpgadl_ser",
++ },
++};
++
++static int __init fpgadl_ser_init(void)
++{
++ int res;
++
++ DBGMSG_ENTER();
++
++ /* Register with the driver core. */
++ res = fpgadl_register_driver(&fpgadl_ser_driver);
++ if (res) {
++ FAILMSG("Can't register fpgadl serial driver");
++ return res;
++ }
++
++ return spi_register_driver(&fpgadl_ser_spi_driver);
++}
++module_init(fpgadl_ser_init);
++
++static void __exit fpgadl_ser_exit(void)
++{
++ DBGMSG_ENTER();
++ spi_unregister_driver(&fpgadl_ser_spi_driver);
++ fpgadl_unregister_driver(&fpgadl_ser_driver);
++}
++module_exit(fpgadl_ser_exit);
++
++MODULE_DESCRIPTION("FPGA serial programming driver");
++MODULE_AUTHOR("Hugo Villeneuve");
++MODULE_LICENSE("GPL");
+diff --git a/include/linux/fpgadl.h b/include/linux/fpgadl.h
+new file mode 100644
+index 0000000..27d83f1
+--- /dev/null
++++ b/include/linux/fpgadl.h
+@@ -0,0 +1,96 @@
++/*
++ * 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 FPGADL_H
++#define FPGADL_H 1
++
++#include <linux/miscdevice.h>
++
++/* FPGA device-specific informations and functions. */
++struct fpgadl_pdata_t {
++ enum {
++ FPGA_VENDOR_XILINX,
++ FPGA_VENDOR_ALTERA,
++ } fpga_vendor;
++ enum {
++ FPGA_FAMILY_XILINX_XC3S,
++ FPGA_FAMILY_XILINX_XC4V,
++ } fpga_family;
++ ssize_t payload_full_size;
++ u8 program_b;
++ u8 init_b;
++ u8 done;
++ char *bitstream_name;
++ int check_init_low; /* Set to 1 to check that the INIT pin is low during
++ * programming. 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. */
++};
++
++struct fpgadl_driver {
++ char *version;
++ struct module *module;
++ struct device_driver driver;
++ struct driver_attribute version_attr;
++};
++
++struct fpgadl_device {
++ int id;
++ char *name;
++ enum {
++ FPGADL_DEV_STATE_START,
++ FPGADL_DEV_STATE_DEVICE_REGISTERED,
++ FPGADL_DEV_STATE_GPIO_REGISTERED,
++ FPGADL_DEV_STATE_CHAR_DEV_REGISTERED,
++ } state;
++ int bitstream_buffer_allocated;
++ u8 *bitstream_data;
++ size_t bitstream_length;
++ size_t bitstream_max_size;
++ int bitstream_mode;
++ int bitstream_loaded;
++ int (*write_byte)(struct fpgadl_device *, u8 *, int);
++ void *devdata; /* Pointer to interface-specific (SPI/PAR) device */
++ struct miscdevice miscdev;
++ struct fpgadl_driver *driver;
++ struct fpgadl_pdata_t *pdata;
++ struct device dev;
++};
++
++/* Bitstream types. */
++#define BITSTREAM_MODE_UNKNOWN 0
++#define BITSTREAM_MODE_FULL 1
++#define BITSTREAM_MODE_PARTIAL 2
++
++extern struct bus_type fpgadl_bus_type;
++
++#define to_fpgadl_driver(drv) container_of(drv, struct fpgadl_driver, driver);
++
++extern int fpgadl_register_driver(struct fpgadl_driver *drv);
++extern void fpgadl_unregister_driver(struct fpgadl_driver *drv);
++
++extern int fpgadl_register_device(struct fpgadl_device *fpgadldev);
++extern void fpgadl_unregister_device(struct fpgadl_device *fpgadldev);
++
++int fpgadl_is_bitstream_loaded(const char *name);
++
++#endif /* FPGADL_H */
+--
+1.5.4.5
+