Browse Source

sysmobts: Add drivers for the sysmoBTS

master
Holger Hans Peter Freyther 10 years ago
committed by Harald Welte
parent
commit
7cb009c9eb
  1. 63
      drivers/misc/Kconfig
  2. 6
      drivers/misc/Makefile
  3. 609
      drivers/misc/dspdl.c
  4. 457
      drivers/misc/dspdl_dm644x.c
  5. 272
      drivers/misc/factoryreset.c
  6. 816
      drivers/misc/fpgadl.c
  7. 263
      drivers/misc/fpgadl_par.c
  8. 243
      drivers/misc/fpgadl_ser.c
  9. 82
      include/linux/dspdl-coff.h
  10. 69
      include/linux/dspdl.h
  11. 85
      include/linux/fpgadl.h

63
drivers/misc/Kconfig

@ -766,6 +766,69 @@ config PANEL_BOOT_MESSAGE
An empty message will only clear the display at driver init time. Any other
printf()-formatted message is valid with newline and escape codes.
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.
config DSPDL
tristate "DSP COFF loader support"
help
This option enables support for loading DSP firmware (COFF
source file) inside a DSP.
To compile this driver as a module, choose M here: the
module will be called dspdl.
If unsure, say N.
config DSPDL_DM644X
tristate "DM644x COFF loader driver"
depends on DSPDL
help
Say Y here to build a module for loading the firmware in a DM644x DSP.
To compile this driver as a module, choose M here: the
module will be called dspdl_dm644x
config SYSMOBTS_FACTORY_RESET
tristate "sysmobts v1/v2 Factory Reset support"
depends on MACH_SYSMOBTS_V1 || MACH_SYSMOBTS_V2
help
This option enables support for factory reset button
on the sysmocom sysmobts v1/v2.
To compile this driver as a module, choose M here: the
module will be called factoryreset.
If unsure, say N.
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"

6
drivers/misc/Makefile

@ -32,6 +32,12 @@ obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
obj-$(CONFIG_DS1682) += ds1682.o
obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o
obj-$(CONFIG_C2PORT) += c2port/
obj-$(CONFIG_FPGADL) += fpgadl.o
obj-$(CONFIG_FPGADL_SER) += fpgadl_ser.o
obj-$(CONFIG_FPGADL_PAR) += fpgadl_par.o
obj-$(CONFIG_DSPDL) += dspdl.o
obj-$(CONFIG_DSPDL_DM644X) += dspdl_dm644x.o
obj-$(CONFIG_SYSMOBTS_FACTORY_RESET) += factoryreset.o
obj-$(CONFIG_HMC6352) += hmc6352.o
obj-y += eeprom/
obj-y += cb710/

609
drivers/misc/dspdl.c

@ -0,0 +1,609 @@
/*
* dspdl 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/slab.h>
#include <linux/vmalloc.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/dspdl.h>
#include <linux/dspdl-coff.h>
#include <asm/uaccess.h>
#define MODULE_NAME "dspdl"
/* Define this to enable verbose debug messages */
#define DSPDL_DEBUG 1
static const char dspdl_driver_version[] = "v1.0";
/* Module parameters */
static unsigned int dspdl_debug;
module_param_named(debug, dspdl_debug, int, 0644);
#ifdef DSPDL_DEBUG
#define INFOMSG(fmt, args...) \
do { \
printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ## args); \
} while (0)
#define DBGMSG(fmt, args...) \
do { \
if (dspdl_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 DSPDL_FW_MAX_SIZE 0x800000
#define MAX_DSPDL_DEV 4
static unsigned fw_max_size = DSPDL_FW_MAX_SIZE;
static int dspdl_dev_count;
static struct dspdl_device *dspdl_dev_array[MAX_DSPDL_DEV];
/* Respond to hotplug events. */
static int dspdl_uevent(struct device *dev, struct kobj_uevent_env *env)
{
DBGMSG_ENTER();
if (add_uevent_var(env, "DSPDL_BUS_VERSION=%s", dspdl_driver_version))
return -ENOMEM;
return 0;
};
static int dspdl_fw_load(struct dspdl_device *dspdl_dev,
const u8 *data, size_t size)
{
int i;
struct file_hdr_t *file_hdr;
struct opt_hdr_t *opt_hdr;
struct section_hdr_t *section_hdr;
int ret;
if (size == 0) {
return 0;
}
dspdl_dev->fw_loaded = 0;
DBGMSG("DSP %d: Starting programming", dspdl_dev->id);
if (dspdl_dev->cb_start) {
ret = dspdl_dev->cb_start(dspdl_dev);
if (ret < 0)
return ret;
}
/* Extract header infos. */
file_hdr = (struct file_hdr_t *) data;
if (file_hdr->magic != 0xC2) {
FAILMSG("Invalid magic number in header (0x%02X)",
file_hdr->magic);
return -EFAULT;
}
if (file_hdr->target_id != 0x0099) {
FAILMSG("Invalid target ID in header (0x%04X)",
file_hdr->target_id);
return -EFAULT;
}
/* Make sure file is a valid COFF. */
if (!(file_hdr->flags & F_EXEC)) {
FAILMSG("Not an executable");
return -ENOEXEC;
}
/* The DSPBOOTADDR contains the upper 22 bits of the DSP reset vector.
* This driver creates a reset vector that is always located at the
* very beginning of the DDR2 memory region occupied by the DSP.
* Therefore, the DSP application must not use the first 12 bytes of
* DDR2 memory. */
opt_hdr = (struct opt_hdr_t *) (data + sizeof(struct file_hdr_t));
DBGMSG(" Entry point: $%08X", opt_hdr->entrypt);
/* Points to first Section */
section_hdr = (struct section_hdr_t *)
((void *) opt_hdr + file_hdr->opthdr);
/* Scan each section and copies their data into memory if their flag is
* set to STYP_COPY */
for (i = 0; i < file_hdr->nscns; i++) {
if ((section_hdr->s_size != 0) &&
(section_hdr->s_scnptr != 0) &&
!(section_hdr->s_flags & STYP_COPY)) {
DBGMSG(" Section %02d, name = \"%s\"", i,
section_hdr->s_name);
DBGMSG(" addr = $%08X, size = $%08X",
section_hdr->s_paddr, section_hdr->s_size);
ret = dspdl_dev->cb_write_section(
dspdl_dev, section_hdr->s_paddr,
(u8 *) (data + section_hdr->s_scnptr),
section_hdr->s_size);
if (ret < 0)
return ret;
}
section_hdr++;
}
if (dspdl_dev->cb_finish) {
ret = dspdl_dev->cb_finish(dspdl_dev, opt_hdr->entrypt);
if (ret < 0)
return ret;
}
INFOMSG("DSP %d: Firmware loaded", dspdl_dev->id);
dspdl_dev->fw_loaded = 1;
return 0;
}
/* Open method. */
static int dspdl_open(struct inode *inode, struct file *filp)
{
int k;
int found = 0;
struct dspdl_device *dspdl_dev;
DBGMSG_ENTER();
DBGMSG(" Opening device minor %d", MINOR(inode->i_rdev));
for (k = 0; k < dspdl_dev_count; k++) {
dspdl_dev = dspdl_dev_array[k];
if (dspdl_dev) {
if (dspdl_dev->miscdev.minor == MINOR(inode->i_rdev)) {
found = 1;
break;
}
}
}
if (!found) {
FAILMSG(" Invalid minor device");
return -ENOMEM;
}
filp->private_data = dspdl_dev;
dspdl_dev->fw_length = 0;
#ifdef USE_KMALLOC // Use kmaloc
fw_max_size = DSPDL_FW_MAX_SIZE;
dspdl_dev->fw_data = kmalloc(fw_max_size,
GFP_KERNEL);
if (!dspdl_dev->fw_data) {
fw_max_size -= 0x200000;
dspdl_dev->fw_data = kmalloc(fw_max_size,
GFP_KERNEL);
if (!dspdl_dev->fw_data) {
fw_max_size -= 0x200000;
dspdl_dev->fw_data = kmalloc(fw_max_size,
GFP_KERNEL);
if (!dspdl_dev->fw_data) {
FAILMSG("Failed to allocate memory for firmware");
return -ENOMEM;
}
}
}a
#else // Use vmalloc
fw_max_size = DSPDL_FW_MAX_SIZE;
dspdl_dev->fw_data = vmalloc(fw_max_size);
if (!dspdl_dev->fw_data) {
FAILMSG("Failed to allocate memory for firmware");
return -ENOMEM;
}
#endif
dspdl_dev->fw_buffer_allocated = 1;
return 0;
}
/* Write method. Fill buffer with bitstream data. */
static ssize_t dspdl_write(struct file *filp, const char __user *buff,
size_t count, loff_t *offp)
{
struct dspdl_device *dspdl_dev = filp->private_data;
if ((dspdl_dev->fw_length + count) >= fw_max_size) {
FAILMSG("Bitstream buffer size exceeded");
return -EFBIG;
}
if (copy_from_user(dspdl_dev->fw_data +
dspdl_dev->fw_length,
(void __user *) buff, count))
return -EFAULT;
dspdl_dev->fw_length += count;
return count;
}
/* Release method. This will initiate the DSP programming. */
static int dspdl_release(struct inode *inode, struct file *filp)
{
int retval;
struct dspdl_device *dspdl_dev = filp->private_data;
if (!dspdl_dev->fw_data)
return -EFAULT;
retval = dspdl_fw_load(dspdl_dev,
dspdl_dev->fw_data,
dspdl_dev->fw_length);
#ifdef USE_KMALLOC // Use kmaloc
kfree(dspdl_dev->fw_data);
#else // Use vmalloc
vfree(dspdl_dev->fw_data);
#endif
dspdl_dev->fw_buffer_allocated = 0;
return retval;
}
static long dspdl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int retval;
void __user *argp = (void __user *)arg;
struct dspdl_device *dspdl_dev = filp->private_data;
switch ( cmd )
{
// 32-bit read
case 0:
{
u32 addr, data;
if (copy_from_user(&addr, argp, sizeof(u32)))
return -EFAULT;
if (dspdl_dev->cb_read_mem) {
retval = dspdl_dev->cb_read_mem(dspdl_dev, addr, &data);
if (retval < 0)
return retval;
if (copy_to_user(argp, &data, sizeof(u32)))
return -EFAULT;
break;
}
return -EFAULT;
}
// 32-bit write
case 1:
{
u32 addr, data;
if (copy_from_user(&addr, argp, sizeof(u32)))
return -EFAULT;
if (copy_from_user(&data, argp + sizeof(u32), sizeof(u32)))
return -EFAULT;
if (dspdl_dev->cb_write_mem) {
retval = dspdl_dev->cb_write_mem(dspdl_dev, addr, data);
if (retval < 0)
return retval;
break;
}
return -EFAULT;
}
default:
{
return -EINVAL;
}
}
return 0;
}
static struct file_operations fops_dspdl = {
.owner = THIS_MODULE,
.open = dspdl_open,
.write = dspdl_write,
.unlocked_ioctl = dspdl_ioctl,
.release = dspdl_release,
};
/* Match dspdl devices to drivers. Just do a simple name test. */
static int dspdl_device_match(struct device *dev,
struct device_driver *drv)
{
DBGMSG_ENTER();
return !strncmp(dev_name(dev), drv->name, strlen(drv->name));
}
static ssize_t show_version(struct device_driver *driver, char *buf)
{
struct dspdl_driver *dspdldriver = to_dspdl_driver(driver);
sprintf(buf, "%s\n", dspdldriver->version);
return strlen(buf);
}
int dspdl_register_driver(struct dspdl_driver *drv)
{
int res;
DBGMSG_ENTER();
/* Initialize common driver fields */
drv->driver.bus = &dspdl_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.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(dspdl_register_driver);
void dspdl_unregister_driver(struct dspdl_driver *drv)
{
DBGMSG_ENTER();
driver_unregister(&drv->driver);
}
EXPORT_SYMBOL(dspdl_unregister_driver);
/* The dspdl bus device. */
static void dspdl_bus_release(struct device *dev)
{
DBGMSG_ENTER();
}
struct device dspdl_bus = {
.init_name = "dspdl0",
.release = dspdl_bus_release
};
struct bus_type dspdl_bus_type = {
.name = "dspdl",
.match = dspdl_device_match,
.uevent = dspdl_uevent,
};
EXPORT_SYMBOL(dspdl_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", dspdl_driver_version);
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
/*
* dspdl devices.
* For now, no references to dspdlbus devices go out which are not
* tracked via the module reference count, so we use a no-op
* release function.
*/
static void dspdl_dev_release(struct device *dev)
{
DBGMSG_ENTER();
}
static void dspdl_cleanup(struct dspdl_device *dspdl_dev)
{
DBGMSG_ENTER();
if (!dspdl_dev)
return;
dspdl_dev_array[dspdl_dev->id] = NULL;
/* Get rid of any allocated buffer, not freed */
if (dspdl_dev->fw_buffer_allocated)
#ifdef USE_KMALLOC // Use kmaloc
kfree(dspdl_dev->fw_data);
#else // Use vmalloc
vfree(dspdl_dev->fw_data);
#endif
switch (dspdl_dev->state) {
case DSPDL_DEV_STATE_CHAR_DEV_REGISTERED:
misc_deregister(&dspdl_dev->miscdev);
case DSPDL_DEV_STATE_DEVICE_REGISTERED:
device_unregister(&dspdl_dev->dev);
case DSPDL_DEV_STATE_START:
break;
}
}
int dspdl_register_device(struct dspdl_device *dspdl_dev)
{
int res;
const struct firmware *fw_entry;
DBGMSG_ENTER();
dspdl_dev->state = DSPDL_DEV_STATE_START;
/* Sanity checks. */
if (!dspdl_dev->name) {
FAILMSG(" Error, missing device name");
res = -EFAULT;
goto error;
}
if (!dspdl_dev->cb_write_section) {
FAILMSG(" Error, missing cb_write_section() callback");
res = -ENOMEM;
goto error;
}
if (dspdl_dev_count == MAX_DSPDL_DEV) {
FAILMSG("Maximum number of devices reached (%d)",
dspdl_dev_count);
res = -ENODEV;
goto error;
}
DBGMSG(" device %d", dspdl_dev_count);
/* Set some default values. */
dspdl_dev->fw_loaded = 0;
dspdl_dev->fw_buffer_allocated = 0;
dspdl_dev->dev.bus = &dspdl_bus_type;
dspdl_dev->dev.parent = &dspdl_bus;
dspdl_dev->dev.release = dspdl_dev_release;
dev_set_name(&dspdl_dev->dev, dspdl_dev->name);
res = device_register(&dspdl_dev->dev);
if (res) {
FAILMSG(" device_register() failed");
goto error;
}
dspdl_dev->state = DSPDL_DEV_STATE_DEVICE_REGISTERED;
dspdl_dev->miscdev.name = dspdl_dev->name;
dspdl_dev->miscdev.minor = MISC_DYNAMIC_MINOR;
dspdl_dev->miscdev.fops = &fops_dspdl;
res = misc_register(&dspdl_dev->miscdev);
if (res < 0) {
FAILMSG("Error registering misc driver");
goto error;
}
DBGMSG(" MINOR = %d", dspdl_dev->miscdev.minor);
dspdl_dev->state = DSPDL_DEV_STATE_CHAR_DEV_REGISTERED;
/* Try to load firmware through hotplug if available. */
if (dspdl_dev->fw_name) {
res = request_firmware(&fw_entry, dspdl_dev->fw_name,
&dspdl_dev->dev);
if (res < 0) {
/* Not a critical error. */
res = 0;
DBGMSG("firmware <%s> not available", dspdl_dev->fw_name);
} else {
res = dspdl_fw_load(dspdl_dev, fw_entry->data,
fw_entry->size);
release_firmware(fw_entry);
}
}
dspdl_dev->id = dspdl_dev_count;
dspdl_dev_array[dspdl_dev_count] = dspdl_dev;
dspdl_dev_count++;
return 0;
error:
dspdl_cleanup(dspdl_dev);
return res;
}
EXPORT_SYMBOL(dspdl_register_device);
void dspdl_unregister_device(struct dspdl_device *dspdl_dev)
{
DBGMSG_ENTER();
/* Warning: fix this, potential bug if removing a device in-between. */
dspdl_dev_array[dspdl_dev->id] = NULL;
dspdl_dev_count--;
dspdl_cleanup(dspdl_dev);
}
EXPORT_SYMBOL(dspdl_unregister_device);
static int __init dspdl_init(void)
{
int res;
DBGMSG_ENTER();
INFOMSG("DSP firmware loader %s", dspdl_driver_version);
res = bus_register(&dspdl_bus_type);
if (res) {
FAILMSG(" bus_register() failed");
goto fail_bus;
}
if (bus_create_file(&dspdl_bus_type, &bus_attr_version)) {
FAILMSG("Unable to create version attribute");
goto fail_create_file;
}
res = device_register(&dspdl_bus);
if (res) {
FAILMSG(" failed registering %s", dspdl_bus.init_name);
goto fail_dev_reg;
}
return 0;
fail_dev_reg:
fail_create_file:
bus_unregister(&dspdl_bus_type);
fail_bus:
return res;
}
module_init(dspdl_init);
static void __exit dspdl_exit(void)
{
DBGMSG_ENTER();
device_unregister(&dspdl_bus);
bus_unregister(&dspdl_bus_type);
}
module_exit(dspdl_exit);
MODULE_AUTHOR("Hugo Villeneuve <hvilleneuve@lyrtech.com>");
MODULE_DESCRIPTION("DSP loader driver");
MODULE_LICENSE("GPL");

457
drivers/misc/dspdl_dm644x.c

@ -0,0 +1,457 @@
/*
* dspdl_dm644x.c - DM644X DSP loader 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/slab.h>
#include <linux/delay.h>
#include <linux/dspdl.h>
#include <linux/dma-mapping.h>
#include <asm/gpio.h> /* For ioremap() */
#include <asm/io.h>
#include <mach/hardware.h>
#include <../../arch/arm/mach-davinci/psc.h>
/* including davinci.h is difficult */
#define DAVINCI_SYSTEM_MODULE_BASE 0x01c40000
#define MODULE_NAME "dspdl_dm644x"
#define MODULE_VERSION_STR "v1.1"
/* Define this to enable verbose debug messages */
#define DSPDL_DM644X_DEBUG 1
/* Module parameters */
static unsigned int dspdl_dm644x_debug;
module_param_named(debug, dspdl_dm644x_debug, int, 0644);
static char *dspdl_dm644x_fw_name = NULL;
module_param_named(fw_name, dspdl_dm644x_fw_name, charp, 0644);
#ifdef DSPDL_DM644X_DEBUG
#define INFOMSG(fmt, args...) \
do { \
printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ## args); } while (0)
#define DBGMSG(fmt, args...) \
do { if (dspdl_dm644x_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)
/*
* PSC register offsets
* already defined in arch/arm/mach-davinci/psc.c
*/
#define DAVINCI_PWR_SLEEP_CNTRL_BASE 0x01C41000
#define EPCPR 0x070
#define PTCMD 0x120
#define PTSTAT 0x128
#define PDSTAT 0x200
#define PDCTL1 0x304
#define MDSTAT 0x800
#define MDCTL 0xA00
#define PSC_PID (DAVINCI_PWR_SLEEP_CNTRL_BASE + 0)
#define PSC_EPCPR (DAVINCI_PWR_SLEEP_CNTRL_BASE + EPCPR)
#define PSC_PTCMD (DAVINCI_PWR_SLEEP_CNTRL_BASE + PTCMD)
#define PSC_PTSTAT (DAVINCI_PWR_SLEEP_CNTRL_BASE + PTSTAT)
#define PSC_PDCTL1 (DAVINCI_PWR_SLEEP_CNTRL_BASE + PDCTL1)
#define PSC_MDSTAT_DSP (DAVINCI_PWR_SLEEP_CNTRL_BASE + MDSTAT + \
(4 * DAVINCI_LPSC_GEM))
#define PSC_MDCTL_DSP (DAVINCI_PWR_SLEEP_CNTRL_BASE + MDCTL + \
(4 * DAVINCI_LPSC_GEM))
#define PSC_MDSTAT_IMCOP (DAVINCI_PWR_SLEEP_CNTRL_BASE + MDSTAT + \
(4 * DAVINCI_LPSC_IMCOP))
#define PSC_MDCTL_IMCOP (DAVINCI_PWR_SLEEP_CNTRL_BASE + MDCTL + \
(4 * DAVINCI_LPSC_IMCOP))
#define DSPBOOTADDR (DAVINCI_SYSTEM_MODULE_BASE + 8)
/*
* 0 = Assert local reset
* 1 = De-assert local reset
*/
#define PSC_MDCTL_LRST_BIT (1 << 8)
struct dspdl_dm644x_dev_t {
char devname[32];
enum {
DSPDL_DM644X_DEV_STATE_STRUCT_ALLOCATED,
DSPDL_DM644X_DEV_STATE_HAVE_IOREMAP_L1D,
DSPDL_DM644X_DEV_STATE_HAVE_IOREMAP_L1P,
DSPDL_DM644X_DEV_STATE_HAVE_IOREMAP_L2,
DSPDL_DM644X_DEV_STATE_HAVE_IOREMAP_DDR,
DSPDL_DM644X_DEV_STATE_DSPDL_DEV_REGISTERED,
} state;
void *l1d_mmio;
struct resource *l1d_mmio_res;
void *l1p_mmio;
struct resource *l1p_mmio_res;
void *l2_mmio;
struct resource *l2_mmio_res;
void *ddr_mmio;
struct resource *ddr_mmio_res;
struct dspdl_device dspdl_dev;
};
#define MAX_DSPDL_DM644X_DEV 5
static int dspdl_dm644x_dev_count;
static void dsp_reset(int active)
{
u32 mdctl;
void __iomem *psc_base = IO_ADDRESS(DAVINCI_PWR_SLEEP_CNTRL_BASE);
mdctl = __raw_readl(psc_base + MDCTL + 4 * DAVINCI_LPSC_GEM);
if (active)
mdctl &= ~PSC_MDCTL_LRST_BIT;
else
mdctl |= PSC_MDCTL_LRST_BIT;
__raw_writel(mdctl, psc_base + MDCTL + 4 * DAVINCI_LPSC_GEM);
}
static int dm644x_start(struct dspdl_device *dspdl_dev)
{
DBGMSG_ENTER();
/* Put DSP in reset */
dsp_reset(1);
return 0;
}
static int dm644x_finish(struct dspdl_device *dspdl_dev, u32 entry_point)
{
void __iomem *dspbootaddr_reg = IO_ADDRESS(DSPBOOTADDR);
DBGMSG_ENTER();
/* Verify if the DSP boot address first 10 bits are zero. */
if ((entry_point & 0x000003FF) != 0x000) {
FAILMSG("Application entry point not aligned on a 10 bits boundary.");
return -EFAULT;
}
__raw_writel(entry_point, dspbootaddr_reg);
DBGMSG(" Taking DSP out of reset");
dsp_reset(0);
return 0;
}
static int dm644x_write_section(struct dspdl_device *dspdl_dev, u32 start_addr,
u8 *data, ssize_t size)
{
u32 offset;
struct dspdl_dm644x_dev_t *dspdl_dm644x_dev =
(struct dspdl_dm644x_dev_t *) dspdl_dev->devdata;
DBGMSG_ENTER();
if ( (dspdl_dm644x_dev->l1d_mmio_res) &&
(start_addr >= dspdl_dm644x_dev->l1d_mmio_res->start) &&
(start_addr <= dspdl_dm644x_dev->l1d_mmio_res->end)) {
if ((start_addr+size) > dspdl_dm644x_dev->l1d_mmio_res->end) {
FAILMSG("Section exceeds L1D memory region.");
return -EFAULT;
}
offset = start_addr - dspdl_dm644x_dev->l1d_mmio_res->start;
memcpy(dspdl_dm644x_dev->l1d_mmio + offset, data, size);
if (memcmp(dspdl_dm644x_dev->l1d_mmio + offset, data, size)) {
FAILMSG("Failed to write into L1D memory region.");
return -EFAULT;
}
}
else if ( (dspdl_dm644x_dev->l1p_mmio_res) &&
(start_addr >= dspdl_dm644x_dev->l1p_mmio_res->start) &&
(start_addr <= dspdl_dm644x_dev->l1p_mmio_res->end)) {
if ((start_addr+size) > dspdl_dm644x_dev->l1p_mmio_res->end) {
FAILMSG("Section exceeds L1P memory region.");
return -EFAULT;
}
offset = start_addr - dspdl_dm644x_dev->l1p_mmio_res->start;
memcpy(dspdl_dm644x_dev->l1p_mmio + offset, data, size);
if (memcmp(dspdl_dm644x_dev->l1p_mmio + offset, data, size)) {
FAILMSG("Failed to write into L1P memory region.");
return -EFAULT;
}
}
else if ( (dspdl_dm644x_dev->l2_mmio_res) &&
(start_addr >= dspdl_dm644x_dev->l2_mmio_res->start) &&
(start_addr <= dspdl_dm644x_dev->l2_mmio_res->end)) {
if ((start_addr+size) > dspdl_dm644x_dev->l2_mmio_res->end) {
FAILMSG("Section exceeds L2 memory region.");
return -EFAULT;
}
offset = start_addr - dspdl_dm644x_dev->l2_mmio_res->start;
memcpy(dspdl_dm644x_dev->l2_mmio + offset, data, size);
if (memcmp(dspdl_dm644x_dev->l2_mmio + offset, data, size)) {
FAILMSG("Failed to write into L2 memory region.");
return -EFAULT;
}
}
else if ( (dspdl_dm644x_dev->ddr_mmio_res) &&
(start_addr >= dspdl_dm644x_dev->ddr_mmio_res->start) &&
(start_addr <= dspdl_dm644x_dev->ddr_mmio_res->end)) {
if ((start_addr+size) > dspdl_dm644x_dev->ddr_mmio_res->end) {
FAILMSG("Section exceeds DDR memory region.");
return -EFAULT;
}
offset = start_addr - dspdl_dm644x_dev->ddr_mmio_res->start;
memcpy(dspdl_dm644x_dev->ddr_mmio + offset, data, size);
if (memcmp(dspdl_dm644x_dev->ddr_mmio + offset, data, size)) {
FAILMSG("Failed to write into DDR memory region.");
return -EFAULT;
}
}
else {
FAILMSG("Invalid destination address.");
return -EFAULT;
}
return 0;
}
static void dspdl_dm644x_cleanup(struct dspdl_dm644x_dev_t *dev)
{
DBGMSG_ENTER();
if (!dev)
return;
switch (dev->state) {
case DSPDL_DM644X_DEV_STATE_DSPDL_DEV_REGISTERED:
dspdl_unregister_device(&dev->dspdl_dev);
case DSPDL_DM644X_DEV_STATE_HAVE_IOREMAP_DDR:
iounmap(dev->ddr_mmio);
case DSPDL_DM644X_DEV_STATE_HAVE_IOREMAP_L2:
iounmap(dev->l2_mmio);
case DSPDL_DM644X_DEV_STATE_HAVE_IOREMAP_L1P:
iounmap(dev->l1p_mmio);
case DSPDL_DM644X_DEV_STATE_HAVE_IOREMAP_L1D:
iounmap(dev->l1d_mmio);
case DSPDL_DM644X_DEV_STATE_STRUCT_ALLOCATED:
kfree(dev);
break;
}
}
static int dspdl_dm644x_probe(struct platform_device *pdev)
{
int len;
int res;
struct dspdl_dm644x_dev_t *dev = NULL;
DBGMSG_ENTER();
if (dspdl_dm644x_dev_count == MAX_DSPDL_DM644X_DEV) {
FAILMSG("Maximum number of devices reached (%d)",
dspdl_dm644x_dev_count);
res = -ENODEV;
goto error;
}
DBGMSG(" device %d", dspdl_dm644x_dev_count);
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
FAILMSG("Failed to allocate device structure");
res = -ENOMEM;
goto error;
}
dev->state = DSPDL_DM644X_DEV_STATE_STRUCT_ALLOCATED;
//pdev->dev.driver_data = dev; /* Private driver data */
/* Assign virtual addresses to L1D memory regions. */
dev->l1d_mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"l1d");
if (dev->l1d_mmio_res) {
len = dev->l1d_mmio_res->end - dev->l1d_mmio_res->start;
dev->l1d_mmio = ioremap(dev->l1d_mmio_res->start, len);
if (!dev->l1d_mmio) {
FAILMSG("Can't remap l1d_mmio register");
res = -ENXIO;
goto error;
}
}
dev->state = DSPDL_DM644X_DEV_STATE_HAVE_IOREMAP_L1D;
/* Assign virtual addresses to L1P memory regions. */
dev->l1p_mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"l1p");
if (dev->l1p_mmio_res) {
len = dev->l1p_mmio_res->end - dev->l1p_mmio_res->start;
dev->l1p_mmio = ioremap(dev->l1p_mmio_res->start, len);
if (!dev->l1p_mmio) {
FAILMSG("Can't remap l1p_mmio register");
res = -ENXIO;
goto error;
}
}
dev->state = DSPDL_DM644X_DEV_STATE_HAVE_IOREMAP_L1P;
/* Assign virtual addresses to L2 memory regions. */
dev->l2_mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"l2");
if (dev->l2_mmio_res) {
len = dev->l2_mmio_res->end - dev->l2_mmio_res->start;
dev->l2_mmio = ioremap(dev->l2_mmio_res->start, len);
if (!dev->l2_mmio) {
FAILMSG("Can't remap l2_mmio register");
res = -ENXIO;
goto error;
}
}
dev->state = DSPDL_DM644X_DEV_STATE_HAVE_IOREMAP_L2;
/* Assign virtual addresses to DDR memory regions. */
dev->ddr_mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"ddr");
if (dev->ddr_mmio_res) {
len = dev->ddr_mmio_res->end - dev->ddr_mmio_res->start;
dev->ddr_mmio = ioremap(dev->ddr_mmio_res->start, len);
if (!dev->ddr_mmio) {
FAILMSG("Can't remap ddr_mmio register");
res = -ENXIO;
goto error;
}
}
dev->state = DSPDL_DM644X_DEV_STATE_HAVE_IOREMAP_DDR;
if (dspdl_dm644x_fw_name)
dev->dspdl_dev.fw_name = dspdl_dm644x_fw_name;
dev->dspdl_dev.cb_start = dm644x_start;
dev->dspdl_dev.cb_finish = dm644x_finish;
dev->dspdl_dev.cb_write_section = dm644x_write_section;
sprintf(dev->devname, "%s_%d", MODULE_NAME, dspdl_dm644x_dev_count);
DBGMSG(" NAME = %s", dev->devname);
dev->dspdl_dev.name = dev->devname;
dev->dspdl_dev.devdata = dev; /* For our callbacks */
res = dspdl_register_device(&dev->dspdl_dev);
if (res < 0) {
FAILMSG("Error registering dspdl_dm644x device");
goto error;
}
dev->state = DSPDL_DM644X_DEV_STATE_DSPDL_DEV_REGISTERED;
dspdl_dm644x_dev_count++;
return 0;
error:
dspdl_dm644x_cleanup(dev);
return res;
}
static int dspdl_dm644x_remove(struct platform_device *pdev)
{
struct dspdl_dm644x_dev_t *dev = platform_get_drvdata(pdev);
DBGMSG_ENTER();
dspdl_dm644x_cleanup(dev);
return 0;
}
static struct dspdl_driver dspdl_dm644x_driver = {
.version = MODULE_VERSION_STR,
.module = THIS_MODULE,
.driver = {
.name = "dspdl_dm644x",
},
};
static struct platform_driver dspdl_platform_driver = {
.driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
},
.remove = dspdl_dm644x_remove,
};
static int dspdl_dm644x_init(void)
{
int res;
DBGMSG_ENTER();
INFOMSG("DM644X DSP firmware loader %s", MODULE_VERSION_STR);
/* Register with the driver core. */
res = dspdl_register_driver(&dspdl_dm644x_driver);
if (res) {
FAILMSG("Can't register dspdl DM644X driver");
return res;
}
/* The probe function will be called for each platform device declared
* in board setup code. */
res = platform_driver_probe(&dspdl_platform_driver,
dspdl_dm644x_probe);
if (res) {
FAILMSG("platform_driver_probe() failed");
return res;
}
return 0;
}
module_init(dspdl_dm644x_init);
static void __exit dspdl_dm644x_exit(void)
{
DBGMSG_ENTER();
platform_driver_unregister(&dspdl_platform_driver);
dspdl_unregister_driver(&dspdl_dm644x_driver);
}
module_exit(dspdl_dm644x_exit);
MODULE_AUTHOR("Hugo Villeneuve <hvilleneuve@lyrtech.com>");
MODULE_DESCRIPTION("DM644X DSP loader driver");
MODULE_LICENSE("GPL");

272
drivers/misc/factoryreset.c

@ -0,0 +1,272 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <mach/irqs.h>
#include <mach/hardware.h>
#include <mach/common.h>
#include <mach/mux.h>
#include <asm/mach-types.h>
#include <asm/gpio.h>
#include <asm/io.h>
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
#endif /* CONFIG_PROC_FS */
#define MODULE_NAME "factoryreset"
#define FACTORY_RESET_GPIO GPIO(38)
static const char factoryreset_driver_version[] = "v1.0";
static int factoryReset = 0;
struct miscdevice *mdev = NULL;
#ifdef CONFIG_PROC_FS
static void *factoryreset_seq_start(struct seq_file *m, loff_t *pos)
{
return *pos < 1 ? (void *)1 : NULL;
}
static void *factoryreset_seq_next(struct seq_file *m, void *v, loff_t *pos)
{
++*pos;
return NULL;
}
static void factoryreset_seq_stop(struct seq_file *m, void *v)
{
}
static int factoryreset_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "%d", factoryReset);
return 0;
}
static const struct seq_operations factoryreset_proc_op = {
.start = factoryreset_seq_start,
.next = factoryreset_seq_next,
.stop = factoryreset_seq_stop,
.show = factoryreset_proc_show
};
static int factoryreset_proc_open(struct inode *inode, struct file *file)
{
int ret;
struct seq_file *m;
ret = seq_open(file, &factoryreset_proc_op);
if (ret < 0)
return ret;
m = file->private_data;
m->private = mdev;
return 0;
}
static ssize_t factoryreset_proc_write(struct file *file, const char __user *buf,
size_t size, loff_t *ppos)
{
char *kbuf;
int ret = 0;
if (size <= 1 || size >= PAGE_SIZE)
return -EINVAL;
kbuf = kmalloc(size + 1, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
if (copy_from_user(kbuf, buf, size) != 0) {
kfree(kbuf);
return -EFAULT;
}
kbuf[size] = 0;
factoryReset = simple_strtoul(kbuf, 0, 0);
ret = size;
kfree(kbuf);
return ret;
}
static const struct file_operations factoryreset_proc_fops = {
.open = factoryreset_proc_open,
.write = factoryreset_proc_write,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.owner = THIS_MODULE,
};
#endif /* CONFIG_PROC_FS */
static int factoryreset_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int factoryreset_release(struct inode *inode, struct file *filp)
{
return 0;
}
static int factoryreset_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
{
int ret;
ret = copy_to_user(buf, (void *)&factoryReset, 1);
return 1;
}
static int factoryreset_write(struct file *filp, const char *buf,
size_t count, loff_t *ppos)
{
if (copy_from_user((void *)&factoryReset, buf, 1) != 0) {
return -EFAULT;
}
return 1;
}
static const struct file_operations factoryreset_fops = {
.open = factoryreset_open,
.release = factoryreset_release,
.read = factoryreset_read,
.write = factoryreset_write,
.owner = THIS_MODULE,
};
static int factoryreset_probe(struct platform_device *pdev)
{
int ret = -ENODEV;
struct proc_dir_entry *ent;
ret = gpio_request(FACTORY_RESET_GPIO, "factory_reset");
if (ret != 0) {
goto error;
}
ret = gpio_direction_input(FACTORY_RESET_GPIO);
if (ret != 0) {
goto error;
}
factoryReset = (gpio_get_value(FACTORY_RESET_GPIO) == 0);
/* Alloc memory for the misc device */
mdev = kzalloc(sizeof(struct miscdevice), GFP_KERNEL);
if (!mdev) {
printk(KERN_ERR "factoryreset: Failed to allocate misc device structure\n");
ret = -ENOMEM;
goto error;
}
mdev->name = MODULE_NAME;
mdev->minor = MISC_DYNAMIC_MINOR;
mdev->fops = &factoryreset_fops;
ret = misc_register(mdev);
if (ret < 0) {
printk(KERN_ERR "factoryreset: Error registering misc driver\n");
goto error;
}
/* Create PROC Entry */
#ifdef CONFIG_PROC_FS
{
ent = proc_create(MODULE_NAME, 0, NULL, &factoryreset_proc_fops);
if (!ent) {
printk(KERN_ERR "factoryreset: failed to create proc entry\n");
}
}
#endif
printk(KERN_INFO "factoryreset: probed\n");
return 0;
error:
printk(KERN_ERR "factoryreset: probe failed\n");
return ret;
}
static int factoryreset_remove(struct platform_device *pdev)
{
#ifdef CONFIG_PROC_FS
{
remove_proc_entry(MODULE_NAME, NULL);
}
#endif
if (mdev) {
misc_deregister(mdev);
kfree(mdev);
mdev = NULL;
}
gpio_free(FACTORY_RESET_GPIO);
return 0;
}
static void factoryreset_platform_release(struct device *device)
{
}
static struct platform_driver factoryreset_driver = {
.probe = factoryreset_probe,
.remove = factoryreset_remove,
.driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
},
};
static struct platform_device factoryreset_pdevice = {
.name = MODULE_NAME,
.id = 1,
.dev = {
.release = factoryreset_platform_release,
}
};
static int __init factoryreset_init(void)
{
int err = 0;
err = platform_driver_register(&factoryreset_driver);
if ( err < 0 ) {
printk( KERN_ERR "Failed to register factoryreset driver\n" );
return err;
}
err = platform_device_register(&factoryreset_pdevice);
if ( err < 0 ) {
platform_driver_unregister( &factoryreset_driver );
printk( KERN_ERR "Failed to register factoryreset driver\n" );
return err;
}
return err;
}
static void __exit factoryreset_cleanup(void)
{
platform_device_unregister(&factoryreset_pdevice);
platform_driver_unregister(&factoryreset_driver);
}
module_init(factoryreset_init);
module_exit(factoryreset_cleanup);
MODULE_DESCRIPTION("Factory Reset Driver");
MODULE_AUTHOR("Lyrtech RD Inc. <www.lyrtech.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" MODULE_NAME);

816
drivers/misc/fpgadl.c

@ -0,0 +1,816 @@
/*
* 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/firmware.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/fs.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;
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 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;
};
struct bitstream_info {
char strStatus[32]; ///< Status
char strDesignName[129]; ///< Design name
char strPartName[33]; ///< Part name
char strDate[11]; ///< Date
char strTime[9]; ///< Time
u32 u32Length; ///< Bitstream length
u8 *pu8RawBits; ///< Raw bitstream pointer
};
#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->fw_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;
}
/*
* Return value:
* 0: Error
* 1: Full bitstream
* 2: Partial bitstream
*/
static int bitstream_parse_header(const u8 *buffer, size_t length,
struct bitstream_info *pbInfo)
{
u32 flength;
memset(pbInfo, 0, sizeof(struct bitstream_info));
// Skip the heasder
buffer += 13;
// Design name
if ( *buffer++ != 'a' )
{
DBGMSG(" Invalid file format.\n");
return 0;
}
flength = (*buffer++ << 8);
flength |= *buffer++;
memcpy(pbInfo->strPartName, buffer, flength);
buffer += flength;
// Part name
if ( *buffer++ != 'b' )
{
DBGMSG(" Invalid file format.\n");
return 0;
}
flength = (*buffer++ << 8);