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