From: Hugo Villeneuve <[EMAIL PROTECTED]>

ARM DaVinci: Add FPGA driver for the Lyrtech SFFSDR board

This driver loads a bitstream into the FPGA using the SelectMAP parallel 
interface

Signed-off-by: Hugo Villeneuve <[EMAIL PROTECTED]>
---
 drivers/char/Kconfig                      |   12 +
 drivers/char/Makefile                     |    2 +
 drivers/char/sffsdr_fpga/Makefile         |    6 +
 drivers/char/sffsdr_fpga/bitstream_load.c |  353 ++++++++++++++++++++++++++
 drivers/char/sffsdr_fpga/bitstream_load.h |   53 ++++
 drivers/char/sffsdr_fpga/chardev.c        |  387 +++++++++++++++++++++++++++++
 drivers/char/sffsdr_fpga/chardev.h        |   53 ++++
 drivers/char/sffsdr_fpga/common.h         |   43 ++++
 drivers/char/sffsdr_fpga/sffsdr_fpga0.h   |   72 ++++++
 9 files changed, 981 insertions(+), 0 deletions(-)

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index caff851..8668730 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -1104,5 +1104,17 @@ config DEVPORT
 
 source "drivers/s390/char/Kconfig"
 
+config LYRTECH_SFFSDR_FPGA
+       tristate "Lyrtech SFFSDR FPGA Driver Support"
+       depends on ARCH_DAVINCI && MACH_DAVINCI_SFFSDR
+       help
+         This option enables support for the Lyrtech SFFSDR
+         FPGA access driver.
+
+         To compile this driver as a module, choose M here: the
+         module will be called sffsdr_fpga.
+
+         If unsure, say N.
+
 endmenu
 
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 6850f6d..3b62030 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -106,6 +106,8 @@ obj-$(CONFIG_IPMI_HANDLER)  += ipmi/
 obj-$(CONFIG_HANGCHECK_TIMER)  += hangcheck-timer.o
 obj-$(CONFIG_TCG_TPM)          += tpm/
 
+obj-$(CONFIG_LYRTECH_SFFSDR_FPGA)      += sffsdr_fpga/
+
 obj-$(CONFIG_PS3_FLASH)                += ps3flash.o
 
 obj-$(CONFIG_JS_RTC)           += js-rtc.o
diff --git a/drivers/char/sffsdr_fpga/Makefile 
b/drivers/char/sffsdr_fpga/Makefile
new file mode 100644
index 0000000..e6c6a97
--- /dev/null
+++ b/drivers/char/sffsdr_fpga/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Lyrtech SFFSDR FPGA access driver
+#
+
+obj-$(CONFIG_LYRTECH_SFFSDR_FPGA) += sffsdr_fpga.o
+sffsdr_fpga-objs := bitstream_load.o chardev.o
diff --git a/drivers/char/sffsdr_fpga/bitstream_load.c 
b/drivers/char/sffsdr_fpga/bitstream_load.c
new file mode 100644
index 0000000..a0a5303
--- /dev/null
+++ b/drivers/char/sffsdr_fpga/bitstream_load.c
@@ -0,0 +1,353 @@
+/*
+ * FPGA bitstream loader on the
+ * SFF-SDR development board.
+ *
+ * Copyright (C) 2008 Lyrtech <www.lyrtech.com>
+ *
+ * The FPGA is loaded using the SelectMAP mode through
+ * the EMIF interface and some dedicated control signals:
+ *
+ *   FPGA          DM6446
+ *   --------------------
+ *   PROGRAM_B     GPIO37
+ *   DONE          GPIO39
+ *   INIT          GPIO40
+ *   DOUT_BUSY     GPIO42
+ *   CS_B          EMIF_A13 OR CS3n
+ *
+ * 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/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/irq.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+
+#include "common.h"
+#include "sffsdr_fpga0.h"
+#include "bitstream_load.h"
+
+/* Select MAP register address. */
+#define FPGA_SELECT_MAP_REG_OFFSET     0x0000
+
+#define FPGA_DONE_TIMEOUT      100000
+
+#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)
+
+/* The Virtex-4 device only drives BUSY during readback.
+ * Define this to check the state of thwe BUSY pin before each write. */
+#undef SFFSDR_FPGA_CHECK_BUSY_PIN
+
+/* Structure of a TYPE1 packet. */
+struct type1_packet_t {
+       uint32_t word_count:11;
+       uint32_t reserved2:2;
+       uint32_t address:5;
+       uint32_t reserved1:9;
+       uint32_t opcode:2;
+       uint32_t header:3;
+};
+
+/* Structure of a TYPE2 packet. */
+struct type2_packet_t {
+       uint32_t word_count:27;
+       uint32_t opcode:2; /* Reserved. */
+       uint32_t header:3;
+};
+
+static void *fpga_mmio_addr;
+static u8 fpga_program_b_gpio;
+static u8 fpga_done_gpio;
+static u8 fpga_init_gpio;
+static u8 fpga_busy_gpio;
+
+/* Swap bits inside a byte. */
+static u8 swapbits(u8 c)
+{
+       int i;
+       u8 result = 0;
+
+       for (i = 0; i < 8; ++i) {
+               result = result << 1;
+               result |= (c & 1);
+               c = c >> 1;
+       }
+
+       return result;
+}
+
+/*
+ * This function writes a byte of data to the FPGA 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 void select_map_write_byte(u8 data)
+{
+#ifdef SFFSDR_FPGA_CHECK_BUSY_PIN
+       /* Making sure BUSY is high. */
+       if (gpio_get_value(fpga_busy_gpio) == 0)
+               FAILMSG("Error: FPGA BUSY.");
+#endif
+
+       /* We have to swap the bits */
+       FPGA_ACCESS_REG(fpga_mmio_addr + FPGA_SELECT_MAP_REG_OFFSET) =
+               swapbits(data);
+}
+
+/*
+ * This function toggles the CCLK line on the select map
+ * interface the number of times specified by <cycles>.
+ */
+static void select_map_make_clock(int cycles)
+{
+       int k;
+
+       for (k = 0; k < cycles; k++)
+               select_map_write_byte(0);
+}
+
+static int select_map_select_gpio_pins(int board_type)
+{
+       switch (board_type) {
+       case BOARD_TYPE_SFFSDR:
+               fpga_program_b_gpio = GPIO(37);
+               fpga_done_gpio      = GPIO(39);
+               fpga_init_gpio      = GPIO(40);
+               fpga_busy_gpio      = GPIO(42);
+               break;
+       case BOARD_TYPE_VTTI_DAS:
+               /* TODO */
+               break;
+       default:
+               FAILMSG("Error: Unknown board type.");
+               return FPGA_LOAD_INVALID_BOARD_TYPE;
+       }
+
+       return 0;
+}
+
+/* Init DaVinci GPIO to FPGA control pins for the Select MAP mode.
+ * NOTE: This code should be eventually be moved to the board init section. */
+int select_map_init_gpio_pins(int board_type)
+{
+       int retval;
+
+       retval = select_map_select_gpio_pins(board_type);
+       if (retval != 0)
+               goto error;
+
+       /* Configure FPGA PROGRAM_B GPIO. */
+       retval = gpio_request(fpga_program_b_gpio, "fpga_program_b");
+       if (retval == 0) /* FPGA_PROGRAM_B is initially HIGH. */
+               retval = gpio_direction_output(fpga_program_b_gpio, 1);
+       if (retval != 0)
+               goto error;
+
+       /* Configure FPGA INIT GPIO. */
+       retval = gpio_request(fpga_init_gpio, "fpga_init");
+       if (retval == 0)
+               retval = gpio_direction_input(fpga_init_gpio);
+       if (retval != 0)
+               goto error;
+
+       /* Configure FPGA DONE GPIO. */
+       retval = gpio_request(fpga_done_gpio, "fpga_done");
+       if (retval == 0)
+               retval = gpio_direction_input(fpga_done_gpio);
+       if (retval != 0)
+               goto error;
+
+       /* Configure FPGA BUSY GPIO. */
+       retval = gpio_request(fpga_busy_gpio, "fpga_busy");
+       if (retval == 0)
+               retval = gpio_direction_input(fpga_busy_gpio);
+       if (retval != 0)
+               goto error;
+
+       return 0;
+
+error:
+       FAILMSG("Error configuring GPIO pins.");
+       return -1;
+}
+
+/* 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. */
+void select_map_release_gpio_pins(void)
+{
+       gpio_free(fpga_busy_gpio);
+       gpio_free(fpga_done_gpio);
+       gpio_free(fpga_init_gpio);
+       gpio_free(fpga_program_b_gpio);
+}
+
+/*
+ * Return value:
+ *   0: Error
+ *   1: Full bitstream
+ *   2: Partial bitstream
+ */
+static int bitstream_parse_header(const u8 *buffer, size_t length)
+{
+       int index = 0;
+       int found;
+       size_t payload_size = 0;
+
+       /* Search for bitstream sync word. */
+       found = false;
+       while ((index < length) && (found == false)) {
+               if ((buffer[index + 0] == BITSTREAM_SYNC_BYTE1) &&
+                   (buffer[index + 1] == BITSTREAM_SYNC_BYTE2) &&
+                   (buffer[index + 2] == BITSTREAM_SYNC_BYTE3) &&
+                   (buffer[index + 3] == BITSTREAM_SYNC_BYTE4))
+                       found = true;
+               else
+                       index++;
+       }
+
+       if (found == false) {
+               DBGMSG("Error, Xilinx header not found in bitstream file.");
+               return BITSTREAM_MODE_UNKNOWN;
+       }
+
+       /* Find the payload size. */
+       while (index < length) {
+               u32 temp = ntohl(*((u32 *) &buffer[index]));
+               struct type1_packet_t *type1_packet =
+                       (struct type1_packet_t *) &temp;
+
+               /* Search for type 1 packet header. */
+               if ((type1_packet->header == BITSTREAM_PACKET_HEADER_TYPE1) &&
+                   (type1_packet->opcode == BITSTREAM_TYPE1_OPCODE_WRITE) &&
+                   (type1_packet->address == BITSTREAM_TYPE1_REG_ADDR_FDRI)) {
+                       if (type1_packet->word_count != 0) {
+                               payload_size = type1_packet->word_count;
+                               break;
+                       } else {
+                               u32 temp2 = ntohl(*((u32 *) &buffer[index+4]));
+                               struct type2_packet_t *type2_packet =
+                                       (struct type2_packet_t *) &temp2;
+
+                               /* Search for type 2 packet header just after
+                                * type1 packet. */
+                               if ((type2_packet->header ==
+                                    BITSTREAM_PACKET_HEADER_TYPE2)) {
+                                       payload_size = type2_packet->word_count;
+                                       break;
+                               }
+                       }
+               }
+
+               index += 4; /* u32 aligned when sync word has been found. */
+       }
+
+       if (index >= length) {
+               DBGMSG("Error, payload size not found in bitstream file.");
+               return BITSTREAM_MODE_UNKNOWN;
+       }
+
+       payload_size *= 4; /* Length in bytes. */
+
+       DBGMSG("Bitstream payload size: %d bytes", payload_size);
+
+       /* Is it a full or a partial bitstream? */
+       if (payload_size == BITSTREAM_LENGTH_SX35) {
+               DBGMSG("Bitstream type: FULL");
+               return BITSTREAM_MODE_FULL;
+       } else {
+               DBGMSG("Bitstream type: PARTIAL");
+               return BITSTREAM_MODE_PARTIAL;
+       }
+}
+
+/*
+ * Bitstreams supported: Full or Partial.
+ * Note: Full bitstream that supports partial bitstream must be generated with
+ * option Persist = true.
+ */
+int bitstream_load(void *mmio_addr, const u8 *data, size_t size,
+                  int *bitstream_mode)
+{
+       int k;
+       int timeout_counter = 0;
+
+       fpga_mmio_addr = mmio_addr;
+
+       *bitstream_mode = bitstream_parse_header(data, size);
+       switch (*bitstream_mode) {
+       case BITSTREAM_MODE_FULL:
+               /* Toggle PROG_B Pin and wait 300nS before proceeding. */
+               gpio_set_value(fpga_program_b_gpio, 0);
+               udelay(1);
+               break;
+       case BITSTREAM_MODE_PARTIAL:
+               break;
+       case BITSTREAM_MODE_UNKNOWN:
+       default:
+               FAILMSG("Error: Unknown bitstream mode.");
+               return FPGA_LOAD_ERROR;
+               break;
+       }
+
+       /* For partial bitstream, PROGRAM_B is already high. */
+       select_map_make_clock(3);
+       gpio_set_value(fpga_program_b_gpio, 1);
+
+       /* Wait for INIT pin to go high. */
+       while (gpio_get_value(fpga_init_gpio) == 0)
+               select_map_make_clock(3);
+
+       /* Send actual bitstream data to FPGA one byte at a time. */
+       for (k = 0; k < size; k++) {
+               select_map_write_byte(data[k]);
+
+               if (gpio_get_value(fpga_init_gpio) == 0) {
+                       /* Error if INIT goes low during programming. */
+                       FAILMSG("Error: INIT LOW during programming.");
+                       return FPGA_LOAD_INIT_ERROR;
+               }
+       }
+
+       /* Pulse the clock line ten times at the end. */
+       select_map_make_clock(10);
+
+       /* FPGA DONE pin must go high. */
+       while ((gpio_get_value(fpga_done_gpio) == 0) &&
+              (timeout_counter < FPGA_DONE_TIMEOUT))
+               timeout_counter++;
+
+       if (gpio_get_value(fpga_done_gpio) == 0) {
+               /* Timeout error. */
+               FAILMSG("Error: timeout while waiting for DONE to go HIGH.");
+               return FPGA_LOAD_TIMEOUT;
+       }
+
+       return FPGA_LOAD_SUCCESS;
+}
diff --git a/drivers/char/sffsdr_fpga/bitstream_load.h 
b/drivers/char/sffsdr_fpga/bitstream_load.h
new file mode 100644
index 0000000..764c1c3
--- /dev/null
+++ b/drivers/char/sffsdr_fpga/bitstream_load.h
@@ -0,0 +1,53 @@
+/*
+ * 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 BITSTREAM_LOAD_H
+#define BITSTREAM_LOAD_H 1
+
+/* Return codes. */
+#define FPGA_LOAD_SUCCESS      0
+#define FPGA_LOAD_ERROR                -1
+#define FPGA_LOAD_INIT_ERROR   -2
+#define FPGA_LOAD_TIMEOUT      -3
+#define FPGA_LOAD_INVALID_BOARD_TYPE   -4
+
+/* Bitstream types. */
+#define BITSTREAM_MODE_UNKNOWN 0
+#define BITSTREAM_MODE_FULL    1
+#define BITSTREAM_MODE_PARTIAL 2
+
+/* Board types. */
+#define BOARD_TYPE_SFFSDR              0
+#define BOARD_TYPE_VTTI_DAS            1
+#define BOARD_TYPE_VTTI_MINIDAS                2
+
+#define BITSTREAM_LENGTH_SX35  (4*426810) /* Length in bytes of a
+                                           * full bitstream. */
+#define BITSTREAM_LENGTH_LX25  (4*243048) /* Length in bytes of a
+                                           * full bitstream. */
+
+int select_map_init_gpio_pins(int board_type);
+
+void select_map_release_gpio_pins(void);
+
+int bitstream_load(void *mmio_addr, const u8 *data, size_t size,
+                  int *bitstream_mode);
+
+#endif /* BITSTREAM_LOAD_H */
diff --git a/drivers/char/sffsdr_fpga/chardev.c 
b/drivers/char/sffsdr_fpga/chardev.c
new file mode 100644
index 0000000..4c2f88c
--- /dev/null
+++ b/drivers/char/sffsdr_fpga/chardev.c
@@ -0,0 +1,387 @@
+/*
+ * sffsdr_fpga 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/firmware.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+
+#include "common.h"
+#include "bitstream_load.h"
+#include "sffsdr_fpga0.h"
+
+
+/* FPGA physical EMIF register base address. */
+#define FPGA_MEM_REGION_BASE_ADDR      (0x4000000)
+#define FPGA_MEM_REGION_LENGTH         (0x2000000) /* 32M */
+
+/* Size of the buffer holding the bitstream data. */
+#define BITSTREAM_BUFFER_SIZE          (2*1024*1024) /* 2M */
+
+/* Structure containing information specific to each FPGA. */
+struct sffsdr_fpga_dev {
+       enum {
+               FPGA_DEV_INIT_START,
+               FPGA_DEV_INIT_DEVICE_REGISTERED,
+               FPGA_DEV_INIT_DONE,
+       } init_state;
+       struct miscdevice miscdev; /* Misc. device structure. */
+       char *bitstream_name;
+       u8 *bitstream_data;
+       size_t bitstream_length;
+       int bitstream_loaded; /* Set to TRUE when FPGA is loaded.
+                              * FPGA1 can be loaded only if FPGA0
+                              * has been loaded. */
+};
+
+/* Structure containing generic driver informations. */
+struct sffsdr_fpga_private {
+       enum {
+               SFFSDR_FPGA_INIT_START,
+               SFFSDR_FPGA_INIT_HAVE_IOMAPPING,
+               SFFSDR_FPGA_INIT_HAVE_GPIO_PINS,
+               SFFSDR_FPGA_INIT_DONE,
+       } init_state;
+       void *mmio_addr; /* Remapped I/O memory space */
+       struct sffsdr_fpga_dev fpga[2]; /* FPGA0 on Base board.
+                                        * FPGA1 on conversion module. */
+};
+
+static struct sffsdr_fpga_private sffsdr_fpga;
+static const char fpga0_device_name[] = "fpga0";
+static const char fpga1_device_name[] = "fpga1";
+
+
+/* Reset the inside logic of the FPGA according to the
+ * bitstream mode. This is done when the bitstream has
+ * been programmed and is Lyrtech SFF-SDR specific. */
+static void sffsdr_fpga_reset(int bitstream_mode)
+{
+       u32 value;
+
+       if (bitstream_mode == BITSTREAM_MODE_FULL)
+               value = FPGA_FULL_RESET_VAL;
+       else
+               value = FPGA_PARTIAL_RESET_VAL;
+
+       FPGA_ACCESS_REG(sffsdr_fpga.mmio_addr + FPGA_GLOBAL_CTRL_REG_OFFSET) =
+               value;
+       FPGA_ACCESS_REG(sffsdr_fpga.mmio_addr + FPGA_GLOBAL_CTRL_REG_OFFSET) =
+               0;
+}
+
+static int sffsdr_fpga_bitstream_load(struct sffsdr_fpga_dev *fpgadev,
+                                     const u8 *data, size_t size)
+{
+       int retval;
+       int bitstream_mode;
+
+       DBGMSG("Programming bitstream on %s", fpgadev->miscdev.name);
+       DBGMSG("  size = %d kb", size / 1024);
+
+       retval = bitstream_load(sffsdr_fpga.mmio_addr, data, size,
+                               &bitstream_mode);
+       if (retval < 0)
+               FAILMSG("Error, firmware not loaded in FPGA");
+       else {
+               sffsdr_fpga_reset(bitstream_mode);
+
+               /* Specific to SFFSDR FPGA. */
+               DBGMSG("FPGA Revision: %d",
+                      FPGA_ACCESS_REG(sffsdr_fpga.mmio_addr +
+                                      FPGA_REVISION_REG_OFFSET) & 0x0000FFFF);
+
+               /* Light LED DS2 to indicate success. */
+               FPGA_ACCESS_REG(sffsdr_fpga.mmio_addr +
+                               FPGA_LED_CONTROL_REG_OFFSET) = FPGA_DSP2_ON;
+
+               FPGA_ACCESS_REG(sffsdr_fpga.mmio_addr +
+                               FPGA_PLL_CODEC_REG_OFFSET) =
+                       FPGA_DIV_BY_1 | FPGA_FS_44100 | FPGA_SR_STANDARD;
+       }
+
+       return retval;
+}
+
+int sffsdr_fpga_set_codec_fs(int fs)
+{
+       u32 fs_mask;
+
+       switch (fs) {
+       case 32000:
+               fs_mask = FPGA_FS_32000;
+               break;
+       case 44100:
+               fs_mask = FPGA_FS_44100;
+               break;
+       case 48000:
+               fs_mask = FPGA_FS_48000;
+               break;
+       default:
+               FAILMSG("Unsupported sampling frequency");
+               return -EFAULT;
+               break;
+       }
+
+       FPGA_ACCESS_REG(sffsdr_fpga.mmio_addr +
+                       FPGA_PLL_CODEC_REG_OFFSET) =
+               FPGA_DIV_BY_1 | fs_mask | FPGA_SR_STANDARD;
+
+       return 0;
+}
+EXPORT_SYMBOL(sffsdr_fpga_set_codec_fs);
+
+/* Open method.
+ *
+ * return value:
+ *   0 in case of success
+ *   -ENOMEM if unable to allocate memory.
+ */
+int sffsdr_fpga_open(struct inode *inode, struct file *filp)
+{
+       int minor = iminor(inode);
+       struct sffsdr_fpga_dev *fpgadev;
+
+       if (minor == sffsdr_fpga.fpga[0].miscdev.minor)
+               filp->private_data = &sffsdr_fpga.fpga[0];
+       else if (minor == sffsdr_fpga.fpga[1].miscdev.minor)
+               filp->private_data = &sffsdr_fpga.fpga[1];
+       else {
+               FAILMSG("Invalid device with minor number %d.", minor);
+               return -EFAULT;
+       }
+
+       fpgadev = (struct sffsdr_fpga_dev *) filp->private_data;
+
+       DBGMSG("Opening %s", fpgadev->miscdev.name);
+
+       fpgadev->bitstream_length = 0;
+       fpgadev->bitstream_data = kmalloc(BITSTREAM_BUFFER_SIZE, GFP_KERNEL);
+
+       if (!fpgadev->bitstream_data) {
+               FAILMSG("Failed to allocate memory for bitstream");
+               return -ENOMEM;
+       }
+
+       return 0; /* success */
+}
+
+/* Write method. Fill buffer with bitstream data.
+ */
+ssize_t sffsdr_fpga_write(struct file *filp, const char __user *buff,
+                 size_t count, loff_t *offp)
+{
+       struct sffsdr_fpga_dev *fpgadev =
+               (struct sffsdr_fpga_dev *) filp->private_data;
+
+       if ((fpgadev->bitstream_length + count) >= BITSTREAM_BUFFER_SIZE) {
+               FAILMSG("Bitstream buffer size exceeded");
+               return -EFBIG;
+       }
+
+       if (copy_from_user(fpgadev->bitstream_data + fpgadev->bitstream_length,
+                          (void __user *) buff, count))
+               return -EFAULT;
+
+       fpgadev->bitstream_length += count;
+
+       return count;
+}
+
+/* Release method. This will initiate the FPGA programming.
+ *                 Only SX35 is supported for the moment.
+ *
+ * return value: 0.
+ */
+int sffsdr_fpga_release(struct inode *inode, struct file *filp)
+{
+       int retval = -1;
+       struct sffsdr_fpga_dev *fpgadev =
+               (struct sffsdr_fpga_dev *) filp->private_data;
+
+       if (fpgadev->bitstream_data) {
+               if (strcmp(fpgadev->miscdev.name, "fpga1") == 0) {
+#ifndef BYPASS_INIT_FPGA1
+                       DBGMSG("TODO: add support for loading SX25 FPGA");
+#endif
+               } else
+                       retval = sffsdr_fpga_bitstream_load(
+                               fpgadev, fpgadev->bitstream_data,
+                               fpgadev->bitstream_length);
+               kfree(fpgadev->bitstream_data);
+       }
+
+       return retval;
+}
+
+static struct file_operations fops_fpga = {
+       .owner = THIS_MODULE,
+       .open = sffsdr_fpga_open,
+       .write = sffsdr_fpga_write,
+       .release = sffsdr_fpga_release
+};
+
+static void sffsdr_fpga_dev_cleanup(struct sffsdr_fpga_dev *fpgadev)
+{
+       DBGMSG("Removing %s", fpgadev->miscdev.name);
+
+       switch (fpgadev->init_state) {
+       case FPGA_DEV_INIT_DONE:
+               DBGMSG("  FPGA_DEV_INIT_DONE");
+       case FPGA_DEV_INIT_DEVICE_REGISTERED:
+               DBGMSG("  FPGA_DEV_INIT_DEVICE_REGISTERED");
+               misc_deregister(&fpgadev->miscdev);
+       case FPGA_DEV_INIT_START:
+               ; /* Nothing to do. */
+       }
+}
+
+static void sffsdr_fpga_cleanup(void)
+{
+       sffsdr_fpga_dev_cleanup(&sffsdr_fpga.fpga[1]);
+       sffsdr_fpga_dev_cleanup(&sffsdr_fpga.fpga[0]);
+
+       switch (sffsdr_fpga.init_state) {
+       case SFFSDR_FPGA_INIT_DONE:
+               DBGMSG("  SFFSDR_FPGA_INIT_DONE");
+       case SFFSDR_FPGA_INIT_HAVE_GPIO_PINS:
+               DBGMSG("  SFFSDR_FPGA_INIT_HAVE_GPIO_PINS");
+               select_map_release_gpio_pins();
+       case SFFSDR_FPGA_INIT_HAVE_IOMAPPING:
+               DBGMSG("  SFFSDR_FPGA_INIT_HAVE_IOMAPPING");
+               iounmap(sffsdr_fpga.mmio_addr);
+       case SFFSDR_FPGA_INIT_START:
+               ; /* Nothing to do. */
+       }
+}
+
+static int sffsdr_fpga_init_device(struct sffsdr_fpga_dev *fpgadev)
+{
+       const struct firmware *fw_entry;
+       int retval;
+
+       fpgadev->miscdev.minor = MISC_DYNAMIC_MINOR;
+       fpgadev->miscdev.fops = &fops_fpga;
+
+       DBGMSG("Initializing %s", fpgadev->miscdev.name);
+
+       retval = misc_register(&fpgadev->miscdev);
+       if (retval < 0) {
+               FAILMSG("misc_register() failed (%d)", retval);
+               goto error;
+       }
+       fpgadev->init_state = FPGA_DEV_INIT_DEVICE_REGISTERED;
+
+       /* Try to load firmware through hotplug if available. */
+       retval = request_firmware(&fw_entry, fpgadev->bitstream_name,
+                                 fpgadev->miscdev.this_device);
+       if (retval < 0)
+               DBGMSG("Firmware not available");
+       else {
+               retval = sffsdr_fpga_bitstream_load(fpgadev, fw_entry->data,
+                                                   fw_entry->size);
+               release_firmware(fw_entry);
+       }
+
+       fpgadev->init_state = FPGA_DEV_INIT_DONE;
+       return retval;
+
+error:
+       sffsdr_fpga_dev_cleanup(fpgadev);
+       return retval;
+}
+
+int __init sffsdr_fpga_init(void)
+{
+       int retval = -1;
+
+       DBGMSG("sffsdr_fpga_init() called");
+
+       sffsdr_fpga.init_state = SFFSDR_FPGA_INIT_START;
+
+       /* Assign virtual addresses to I/O memory regions. */
+       sffsdr_fpga.mmio_addr = ioremap(FPGA_MEM_REGION_BASE_ADDR,
+                                       FPGA_MEM_REGION_LENGTH);
+       if (sffsdr_fpga.mmio_addr == NULL) {
+               FAILMSG("Failed to remap registers");
+               goto error;
+       }
+
+       sffsdr_fpga.init_state = SFFSDR_FPGA_INIT_HAVE_IOMAPPING;
+
+       /* Initialize DSP to FPGA control pins (GPIOs).
+        * Hardcoded to SFFSDR board type for now. */
+       retval = select_map_init_gpio_pins(BOARD_TYPE_SFFSDR);
+       if (retval < 0)
+               goto error;
+
+       sffsdr_fpga.init_state = SFFSDR_FPGA_INIT_HAVE_GPIO_PINS;
+
+       /* Initializing FPGA0. */
+       sffsdr_fpga.fpga[0].init_state = FPGA_DEV_INIT_START;
+       sffsdr_fpga.fpga[0].bitstream_name = "sffsdr_fpga0.bit";
+       sffsdr_fpga.fpga[0].miscdev.name = fpga0_device_name;
+       retval = sffsdr_fpga_init_device(&sffsdr_fpga.fpga[0]);
+       if (retval < 0)
+               goto error;
+
+       /* Initializing FPGA1. */
+#ifndef BYPASS_INIT_FPGA1
+       sffsdr_fpga.fpga[1].init_state = FPGA_DEV_INIT_START;
+       sffsdr_fpga.fpga[1].bitstream_name = "sffsdr_fpga1.bit";
+       sffsdr_fpga.fpga[1].miscdev.name = fpga1_device_name;
+       retval = sffsdr_fpga_init_device(&sffsdr_fpga.fpga[1]);
+       if (retval < 0)
+               goto error;
+#endif
+
+       sffsdr_fpga.init_state = SFFSDR_FPGA_INIT_DONE;
+
+       return 0;
+
+error:
+       sffsdr_fpga_cleanup();
+       return retval;
+}
+
+void __exit sffsdr_fpga_exit(void)
+{
+       DBGMSG("sffsdr_fpga_exit() called");
+
+       sffsdr_fpga_cleanup();
+}
+
+/***********************************
+ * General module initialization   *
+ ***********************************/
+MODULE_AUTHOR("Hugo Villeneuve <[EMAIL PROTECTED]>");
+MODULE_DESCRIPTION("Lyrtech SFFSDR FPGA driver");
+MODULE_LICENSE("GPL");
+
+module_init(sffsdr_fpga_init);
+module_exit(sffsdr_fpga_exit);
diff --git a/drivers/char/sffsdr_fpga/chardev.h 
b/drivers/char/sffsdr_fpga/chardev.h
new file mode 100644
index 0000000..4fea9e0
--- /dev/null
+++ b/drivers/char/sffsdr_fpga/chardev.h
@@ -0,0 +1,53 @@
+/*
+ *  Header file with ioctl definitions.
+ *
+ *  The declarations here have to be in a header file, because
+ *  they need to be known both to the kernel module
+ *  (in chardev.c) and the user process calling ioctl.
+ *
+ * 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 CHARDEV_H
+#define CHARDEV_H 1
+
+#include <linux/ioctl.h>
+
+/* Use 'h' as magic number */
+#define SFFSDR_IOCTL_MAGIC_NUMBER 'h'
+
+/*
+ * _IOR means that we're creating an ioctl command number for passing
+ *      information from the kernel module to a user process.
+ * _IOW means that we're creating an ioctl command number for passing
+ *      information from a user process to the kernel module.
+ *
+ * First argument:  SFFSDR_FPGA_DEVICE_MAJOR is the major device number we're
+ *                  using.
+ * Second argument: The number of the command (there could be several with
+ *                  different meanings).
+ * Third argument:  The type we want to get from the process to the kernel, or
+ *                  vice-versa.
+ */
+#define IOCTL_SFFSDR_FPGA_READ_REG    _IOW(SFFSDR_IOCTL_MAGIC_NUMBER, 0,\
+                                          int32_t *)
+#define IOCTL_SFFSDR_FPGA_WRITE_REG   _IOW(SFFSDR_IOCTL_MAGIC_NUMBER, 1,\
+                                          int32_t *)
+
+int sffsdr_fpga_set_codec_fs(int fs);
+
+#endif /* CHARDEV_H */
diff --git a/drivers/char/sffsdr_fpga/common.h 
b/drivers/char/sffsdr_fpga/common.h
new file mode 100644
index 0000000..53ac6b8
--- /dev/null
+++ b/drivers/char/sffsdr_fpga/common.h
@@ -0,0 +1,43 @@
+/*
+ * Common macros/definitions.
+ *
+ * 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 COMMON_H
+#define COMMON_H 1
+
+#define MODULE_NAME "sffsdr_fpga"
+
+/* Define this to have verbose debug messages. */
+#define SFFSDR_DEBUG 1
+
+/* Define this to bypass initialisation of fpga1 (not supported yet). */
+#define BYPASS_INIT_FPGA1
+
+#ifdef SFFSDR_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
+
+
+#endif /* COMMON_H */
diff --git a/drivers/char/sffsdr_fpga/sffsdr_fpga0.h 
b/drivers/char/sffsdr_fpga/sffsdr_fpga0.h
new file mode 100644
index 0000000..0ee5070
--- /dev/null
+++ b/drivers/char/sffsdr_fpga/sffsdr_fpga0.h
@@ -0,0 +1,72 @@
+/*
+ * SFFSDR FPGA 0 definitions.
+ *
+ * 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 SFFSDR_FPGA0_H
+#define SFFSDR_FPGA0_H 1
+
+/* Access of FPGA register through EMIF (mmio). */
+#define FPGA_ACCESS_REG(x)     (*((u8 *)(x)))
+
+/* Lyrtech specific register inside FPGA. */
+#define FPGA_REVISION_REG_OFFSET       0x8000
+#define FPGA_GLOBAL_CTRL_REG_OFFSET    0x8040
+#define FPGA_LED_CONTROL_REG_OFFSET    0x8300
+#define FPGA_PLL_CODEC_REG_OFFSET      0x8800
+
+#define FPGA_FULL_RESET_VAL    3
+#define FPGA_PARTIAL_RESET_VAL 2
+
+#define FPGA_DSP2_ON           (1<<0)
+#define FPGA_DSP3_ON           (1<<1)
+#define FPGA_DSP4_ON           (1<<2)
+#define FPGA_DSP5_ON           (1<<3)
+#define FPGA_DSP6_ON           (1<<4)
+
+/* Sampling frequency divider:
+ *   0x00: Divide by 1
+ *   0x01: Divide by 2
+ *   0x10: Divide by 4
+ *   0x11: Reserved
+ */
+#define FPGA_DIV_BY_1          (0<<4)
+#define FPGA_DIV_BY_2          (1<<4)
+#define FPGA_DIV_BY_4          (2<<4)
+#define FPGA_DIV_RSV           (3<<4)
+
+/* Sampling rate selection:
+ *   0x0: Standard sampling rate (default)
+ *   0x1: Double sampling rate
+ */
+#define FPGA_SR_STANDARD       (0<<2)
+#define FPGA_SR_DOUBLE         (1<<2)
+
+/* Sampling frequency selection:
+ *   0x00: 48.0 kHz (PLL 12.288 MHz)
+ *   0x01: 44.1 kHz (PLL 11.2896 MHz)
+ *   0x10: 32.0 kHz (PLL 8.192 MHz)
+ *   0x11: Reserved
+ */
+#define FPGA_FS_48000          (0<<0)
+#define FPGA_FS_44100          (1<<0)
+#define FPGA_FS_32000          (2<<0)
+#define FPGA_FS_RSV            (3<<0)
+
+
+#endif /* SFFSDR_FPGA0_H */

_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to