sysmobts: Add drivers for the sysmoBTS
parent
d785062ef2
commit
7cb009c9eb
|
@ -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"
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -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);
|
||||
|
|
@ -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(< |