我有一个 SPI 设备(又名 SPI 从)驱动程序,我已经在 Atmel AT91 处理器上使用了几年。我目前正在将驱动程序移植到一组新的板上,并正在转移到 Gumstix Duovero CPU。旧的东西都在 Linux 3.8 中运行,新的东西都是来自 gumstix 内核存储库的 3.18.21。
简而言之,系统运行的方式是有一个 FPGA 向 GPIO 引脚发送一个脉冲,以告诉处理器数据已准备好。作为响应,CPU 发送 4 个字节,并读取 4 个字节。SPI 总线时钟设置为 1Mhz,而被称为“数据就绪”的 FPGA 信号以大约 20khz 的频率触发。传输都发生在 PIO 中,因为它们对于 DMA 来说太小了。
我已经调试这个新系统好几天了,我已经不知所措了。我认为中断发生的频率是我问题的症结所在,但我不确定。我觉得中断处理程序可能在驱动程序初始化完成之前触发,但我可能是错的。无论如何,当您启动系统时,会发生以下 3 件事之一:
- 系统因内核恐慌而挂起。(90% 的时间)
- 系统启动,但 spi4 死机。(7% 的时间)
- 一切都按预期启动和工作(3% 的时间)
我对内核的通用 SPI 接口知之甚少,也没有发现任何表明存在问题的东西。旧代码直接调用了一些 Atmel 的 SPI 代码,但我认为即使现在这些代码都已合并到更通用的 spi.h 和 gpio.h 文件中。看下面的 traceback 明显是 NULL 指针的问题,但是我还没找到。
任何反馈表示赞赏。我将在下面发布堆栈跟踪和源代码。
追溯
[ 1.850311] QUICKLOGIC: Init called, allocating memory
[ 1.855712] QUICKLOGIC: Allocating locks
[ 1.859985] QUICKLOGIC: Init SPI messages
[ 1.864349] QUICKLOGIC: spi_register driver complete.
[ 1.871276] QUICKLOGIC: Added device to bus, put_device called.
[ 1.877563] QUICKLOGIC: Looking for Quicklogic data ready trigger on GPIO 110.
[ 1.885131] QUICKLOGIC: GPIO request looks ok, trying to grab an IRQ.
[ 1.891845] QUICKLOGIC: gpio_to_irq says we'll request IRQ 270
[ 1.898925] Unable to handle kernel NULL pointer dereference at virtual address 00000004
[ 1.907379] pgd = c0004000
[ 1.910186] [00000004] *pgd=00000000
[ 1.913940] Internal error: Oops: 805 [#1] SMP ARM
[ 1.918914] Modules linked in:
[ 1.922119] CPU: 1 PID: 703 Comm: spi4 Not tainted 3.18.21 #35
[ 1.928192] task: ee24f140 ti: ee51a000 task.ti: ee51a000
[ 1.933837] PC is at spi_pump_messages+0xec/0x54c
[ 1.938751] LR is at _raw_spin_lock_irqsave+0x48/0x54
[ 1.944030] pc : [<c040e2a0>] lr : [<c05ccec4>] psr: 60000193
[ 1.944030] sp : ee51bed0 ip : c091fda8 fp : ee51a000
[ 1.955993] r10: ee26a308 r9 : 00000001 r8 : 00000000
[ 1.961425] r7 : c111af18 r6 : ee26a32c r5 : ee26a000 r4 : ee26a31c
[ 1.968231] r3 : c1151dac r2 : 00000000 r1 : a0000113 r0 : 00000000
[ 1.975036] Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel
[ 1.982757] Control: 10c5387d Table: 8000404a DAC: 00000015
[ 1.988739] Process spi4 (pid: 703, stack limit = 0xee51a240)
[ 1.994720] Stack: (0xee51bed0 to 0xee51c000)
[ 1.999267] bec0: c111af18 00000000 00000001 ee26a308
[ 2.007812] bee0: ee26a2e8 ee26a31c 00000000 ee26a2e8 c111af18 00000000 00000001 ee26a308
[ 2.016326] bf00: ee51a000 c005b618 ee24f140 00000000 ee2dc800 ee26a2e8 c005b554 00000000
[ 2.024871] bf20: 00000000 00000000 00000000 c005b4e8 00000000 00000000 60000193 ee26a2e8
[ 2.033386] bf40: 00000000 00000000 dead4ead ffffffff ffffffff c095c238 00000000 00000000
[ 2.041900] bf60: c076bef4 ee51bf64 ee51bf64 00000000 00000000 dead4ead ffffffff ffffffff
[ 2.050445] bf80: c095c238 00000000 00000000 c076bef4 ee51bf90 ee51bf90 ee064040 ee2dc800
[ 2.058959] bfa0: c005b420 00000000 00000000 c000e730 00000000 00000000 00000000 00000000
[ 2.067504] bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 2.076019] bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 5726fda2 ebc47f2b
[ 2.084564] [<c040e2a0>] (spi_pump_messages) from [<c005b618>] (kthread_worker_fn+0xc4/0x178)
[ 2.093475] [<c005b618>] (kthread_worker_fn) from [<c005b4e8>] (kthread+0xc8/0xe4)
[ 2.101379] [<c005b4e8>] (kthread) from [<c000e730>] (ret_from_fork+0x14/0x24)
[ 2.108917] Code: eafffff1 e2432024 e5842038 e8930005 (e5802004)
[ 2.115295] ---[ end trace 1b2a03254ba5aba5 ]---
[ 18.854156] BUG: spinlock lockup suspected on CPU#0, swapper/0/1
[ 18.860412] lock: 0xee26a32c, .magic: dead4ead, .owner: spi4/703, .owner_cpu: 1
[ 18.868133] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G D 3.18.21 #35
[ 18.875701] [<c00159ac>] (unwind_backtrace) from [<c001213c>] (show_stack+0x10/0x14)
[ 18.883819] [<c001213c>] (show_stack) from [<c05c708c>] (dump_stack+0x84/0x9c)
[ 18.891357] [<c05c708c>] (dump_stack) from [<c0086fe8>] (do_raw_spin_lock+0xf0/0x194)
[ 18.899536] [<c0086fe8>] (do_raw_spin_lock) from [<c05ccec4>] (_raw_spin_lock_irqsave+0x48/0x54)
[ 18.908721] [<c05ccec4>] (_raw_spin_lock_irqsave) from [<c040d1b8>] (spi_queued_transfer+0x18/0x98)
[ 18.918151] [<c040d1b8>] (spi_queued_transfer) from [<c040d10c>] (spi_async+0x5c/0x64)
[ 18.926422] [<c040d10c>] (spi_async) from [<c0411178>] (grab_spi_data+0xe4/0x170)
[ 18.934234] [<c0411178>] (grab_spi_data) from [<c008e3bc>] (handle_irq_event_percpu+0x3c/0x1d8)
[ 18.943298] [<c008e3bc>] (handle_irq_event_percpu) from [<c008e594>] (handle_irq_event+0x3c/0x5c)
[ 18.952575] [<c008e594>] (handle_irq_event) from [<c00912e0>] (handle_edge_irq+0xec/0x188)
[ 18.961181] [<c00912e0>] (handle_edge_irq) from [<c008da94>] (generic_handle_irq+0x28/0x3c)
[ 18.969909] [<c008da94>] (generic_handle_irq) from [<c03426e8>] (omap_gpio_irq_handler+0x16c/0x224)
[ 18.979339] [<c03426e8>] (omap_gpio_irq_handler) from [<c008da94>] (generic_handle_irq+0x28/0x3c)
[ 18.988616] [<c008da94>] (generic_handle_irq) from [<c008dd78>] (__handle_domain_irq+0x64/0xc8)
[ 18.997680] [<c008dd78>] (__handle_domain_irq) from [<c00086f0>] (gic_handle_irq+0x20/0x60)
[ 19.006378] [<c00086f0>] (gic_handle_irq) from [<c05cd9e4>] (__irq_svc+0x44/0x5c)
[ 19.014190] Exception stack(0xee067d68 to 0xee067db0)
[ 19.019470] 7d60: 00000001 00000001 00000000 ee064b40 c0913a3c ee3b6cc0
[ 19.027984] 7d80: ee3b6cc0 ee107940 0000010e ee22985c 60000113 c11341c4 00000000 ee067db0
[ 19.036529] 7da0: c0081914 c05cd038 20000113 ffffffff
[ 19.041778] [<c05cd9e4>] (__irq_svc) from [<c05cd038>] (_raw_spin_unlock_irq+0x28/0x2c)
[ 19.050140] [<c05cd038>] (_raw_spin_unlock_irq) from [<c01a2394>] (proc_alloc_inum+0x30/0xac)
[ 19.059020] [<c01a2394>] (proc_alloc_inum) from [<c01a2428>] (proc_register+0x18/0x134)
[ 19.067382] [<c01a2428>] (proc_register) from [<c01a2640>] (proc_mkdir_data+0x44/0x6c)
[ 19.075653] [<c01a2640>] (proc_mkdir_data) from [<c0093df0>] (register_irq_proc+0x6c/0x128)
[ 19.084350] [<c0093df0>] (register_irq_proc) from [<c008fbb0>] (__setup_irq+0x264/0x530)
[ 19.092803] [<c008fbb0>] (__setup_irq) from [<c008ffac>] (request_threaded_irq+0xa4/0x130)
[ 19.101440] [<c008ffac>] (request_threaded_irq) from [<c08867b8>] (quicklogic_init+0x588/0x694)
[ 19.110504] [<c08867b8>] (quicklogic_init) from [<c00089c4>] (do_one_initcall+0x80/0x1c8)
[ 19.119049] [<c00089c4>] (do_one_initcall) from [<c084ae40>] (kernel_init_freeable+0x1c4/0x28c)
[ 19.128112] [<c084ae40>] (kernel_init_freeable) from [<c05c341c>] (kernel_init+0x8/0xec)
[ 19.136566] [<c05c341c>] (kernel_init) from [<c000e730>] (ret_from_fork+0x14/0x24)
[ 22.871673] INFO: rcu_sched detected stalls on CPUs/tasks: { 0} (detected by 1, t=2102 jiffies, g=-279, c=-280, q=254)
[ 22.882843] Task dump for CPU 0:
[ 22.886230] swapper/0 R running 0 1 0 0x00000002
[ 22.892852] [<c05c8078>] (__schedule) from [<ee009000>] (0xee009000)
快速逻辑.c
/*
quicklogic.c
Authored by Maxwell Bottiger
Based on examples from Scott Ellis, 2010
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/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/spi/spi.h>
#include <linux/string.h>
#include <linux/dma-mapping.h>
#include <asm/uaccess.h>
#include <linux/semaphore.h> //Mutexes, which seem to have come in from somewhere else.
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/signal.h>
#include <linux/delay.h>
//#include "../../arch/arm/mach-at91/include/mach/gpio.h"
#include <linux/gpio.h>
#define SPI_MSG_LEN 4 // should result in PIO
//#define AT91_PIN_PA24 121
#define AT91_PIN_PA24 110
//#define SPI_TRANSFERS 25000 // transfers in 5 seconds
#define SPI_TRANSFERS 10
#define SPI_BUFF_SIZE 100000 // ~5 seconds
#define USER_BUFF_SIZE 100000 // ~5 seconds of samples a 200us per sample
#define SPI_MSG_BUFFER 10 // really only 2, but 10 saves our souls during startup.
#define READ_SLEEP_MSECS 500 // time to wait between user buffer checks
/*
So, the buffer attempts to read every 0.5 seconds, and a buffer is ready every 5
seconds. Therefore we should never attempt to read more than 10 times. If we
read more than 10x, it probably means the process that had the device open
has exited without closing the file handle. So, count off the number of
iterations, then break if you cross the threshold. */
#define READ_ATTEMPT_THRESHOLD 12
#define SPI_BUS 4
#define SPI_BUS_CS 0
#define SPI_BUS_SPEED 1000000
/*bus speed is defined in board-sam9x5ek.c, which is an atmel thing I think*/
const char this_driver_name[] = "quicklogic";
struct quicklogic_control {
struct spi_message msg[SPI_MSG_BUFFER];
struct spi_transfer transfer[SPI_MSG_BUFFER];
u8 *tx_buff;
u8 *rx_buff;
dma_addr_t tx_dma;
dma_addr_t rx_dma;
spinlock_t* lock;
int dirty;
int active;
};
static struct quicklogic_control quicklogic_b1;
static struct quicklogic_control quicklogic_b2;
struct quicklogic_dev {
spinlock_t spi_lock;
struct semaphore fop_sem;
dev_t devt;
struct cdev cdev;
struct class *class;
struct spi_device *spi_device;
char *user_buff;
u8 test_data;
};
static struct quicklogic_dev quicklogic_dev;
void spi_completion_handler(void* arg)
{
}
irqreturn_t grab_spi_data(int irq,void *dev_id)
{
static struct quicklogic_control* buffer = &quicklogic_b1;
static int i = 0;
static int complete = 0;
int status;
unsigned long flags;
int index;
spinlock_t* mSpinLock;
index = i % SPI_MSG_BUFFER;
spi_message_init(&(buffer -> msg[index]));
mSpinLock = buffer -> lock;
buffer -> transfer[index].tx_buf = quicklogic_b1.tx_buff;
buffer -> transfer[index].rx_buf = buffer -> rx_buff + (i * 4);
buffer -> transfer[index].len = SPI_MSG_LEN;
buffer -> msg[index].complete = spi_completion_handler;
buffer -> msg[index].context = &complete;
//Select insertion point in the buffer, then send an SPI message
spi_message_add_tail(&(buffer->transfer[index]), &(buffer->msg[index]));
spin_lock_irqsave(mSpinLock, flags);
if (quicklogic_dev.spi_device)
status = spi_async(quicklogic_dev.spi_device, &(buffer->msg[index]));
else
status = -ENODEV;
buffer -> active = 1;
i++; //move along to the next sample
if (i>SPI_TRANSFERS) { // end of the buffer
//printk(KERN_INFO "QUICKLOGIC: %i transfers have taken place, switching buffers.\n", i);
buffer -> dirty = 0; // contents are unread
buffer -> active = 0; // buffer is at rest
if (buffer == &quicklogic_b1)
buffer = &quicklogic_b2;
else
buffer = &quicklogic_b1;
i = 0; //reset our counter
complete = 0;
}
spin_unlock_irqrestore(mSpinLock, flags);
return IRQ_HANDLED;
}
int check_buffer(struct quicklogic_control* buffer)
{
unsigned long flags;
int success;
success = 0;
//printk("QUICKLOGIC: In check_buffer\n");
spin_lock_irqsave(buffer -> lock, flags); // lock buffer 1
if (buffer -> active != 1) { // not being filled
if (buffer -> dirty == 0) { // buffer has not yet been read
memcpy(quicklogic_dev.user_buff, buffer -> rx_buff, USER_BUFF_SIZE);
buffer -> dirty = 1;
success = 1;
}
}
//spin_unlock_irqrestore(quicklogic_b1.lock, flags); // unlock buffer 1
spin_unlock_irqrestore(buffer -> lock, flags);
return success;
}
static ssize_t quicklogic_read(struct file *filp, char __user *buff, size_t count,
loff_t *offp)
{
size_t len;
size_t status;
int goodRead;
int extra = 0;
int readAttempts; //5 second samples / 0.5 second attempts means we should iterate no more than 10 times
static size_t remainder = USER_BUFF_SIZE;
goodRead = 0;
//printk("QUICKLOGIC: Inside quicklogic_read, requesting %i bytes\n", count);
if (*offp > 0 && remainder == 0) { // this is the end of a previous read
remainder = USER_BUFF_SIZE;
printk(KERN_ALERT "QUICKLOGIC: offp is greater than 0 and no data is left, returning.\n");
return 0;
}
if (down_interruptible(&quicklogic_dev.fop_sem)) {
printk(KERN_ALERT "QUICKLOGIC: down_interruptible fop_sem failed, returning.\n");
return -ERESTARTSYS;
}
if (remainder == USER_BUFF_SIZE) { // this is a new read
readAttempts = 0;
while (goodRead == 0) {
if (check_buffer(&quicklogic_b1)) {
//printk(KERN_INFO "QUICKLOGIC: Buffer 1 will be returned\n");
goodRead = 1;
}
else if (check_buffer(&quicklogic_b2)) {
//printk(KERN_INFO "QUICKLOGIC: Buffer 2 will be returned\n");
goodRead = 1;
}
else if (readAttempts > READ_ATTEMPT_THRESHOLD) {
printk(KERN_ALERT "QUICKLOGIC: Exceded read threshold, exiting quicklogic_read()\n");
up(&quicklogic_dev.fop_sem);
return 0;
}
else {
//printk(KERN_INFO "QUICKLOGIC: Neither buffer is ready, sleeping instead.\n");
msleep_interruptible(READ_SLEEP_MSECS);
readAttempts++;
}
}
}
/* 1 - trylock b1
2 - if b1 is open, lock and check dirty flag
3 - if clean and locked, copy data to user, set dirty flag, unlock and return
4 - b1 is locked, so lock b2
5 - check the dirty bit - if clean copy data to user, set dirty flag, unlock and return
6 - if b2 is dirty, lock b1 and wait
7 - when lock is released do steps 2 and 3
*/
//TODO need to fix this in the buffer selection code.
//memcpy(quicklogic_dev.user_buff, quicklogic_ctl.rx_buff, SPI_BUFF_SIZE);
//quicklogic_dev.user_buff[SPI_BUFF_SIZE] = '\0'; //probably don't need this.
//len = strlen(quicklogic_dev.user_buff);
len = USER_BUFF_SIZE;
if (count <= remainder) {
extra = copy_to_user(buff, quicklogic_dev.user_buff, count);
//printk("QUICKLOGIC: %i bytes remaining to transfer\n", extra);
remainder -= count;
}
else {
extra = copy_to_user(buff, quicklogic_dev.user_buff, remainder);
//printk("QUICKLOGIC: %i bytes remaining to transfer\n", extra);
count = remainder;
remainder = USER_BUFF_SIZE; // Last transfer, so we need to re-init remainder
}
if (extra) {
printk(KERN_ALERT "QUICKLOGIC: quicklogic_read(): copy_to_user() failed\n");
status = -EFAULT;
} else {
//printk("QUICKLOGIC: Sent data back to user space\n");
*offp += count;
status = count;
}
up(&quicklogic_dev.fop_sem);
//printk("QUICKLOGIC: Returning status=%i\n", status);
return status;
}
static int quicklogic_open(struct inode *inode, struct file *filp)
{
int status = 0;
if (down_interruptible(&quicklogic_dev.fop_sem))
return -ERESTARTSYS;
if (!quicklogic_dev.user_buff) {
quicklogic_dev.user_buff = kmalloc(USER_BUFF_SIZE, GFP_KERNEL);
if (!quicklogic_dev.user_buff)
status = -ENOMEM;
}
up(&quicklogic_dev.fop_sem);
return status;
}
static int quicklogic_probe(struct spi_device *spi_device)
{
unsigned long flags;
spin_lock_irqsave(&quicklogic_dev.spi_lock, flags);
quicklogic_dev.spi_device = spi_device;
spin_unlock_irqrestore(&quicklogic_dev.spi_lock, flags);
return 0;
}
static int quicklogic_remove(struct spi_device *spi_device)
{
unsigned long flags;
spin_lock_irqsave(&quicklogic_dev.spi_lock, flags);
quicklogic_dev.spi_device = NULL;
spin_unlock_irqrestore(&quicklogic_dev.spi_lock, flags);
return 0;
}
static int __init add_quicklogic_device_to_bus(void)
{
struct spi_master *spi_master;
struct spi_device *spi_device;
struct device *pdev;
char buff[64];
int status = 0;
int quicklogic_irq;
spi_master = spi_busnum_to_master(SPI_BUS);
if (!spi_master) {
printk(KERN_ALERT "spi_busnum_to_master(%d) returned NULL\n",
SPI_BUS);
printk(KERN_ALERT "Missing modprobe omap2_mcspi?\n");
return -1;
}
spi_device = spi_alloc_device(spi_master);
if (!spi_device) {
put_device(&spi_master->dev);
printk(KERN_ALERT "spi_alloc_device() failed\n");
return -1;
}
//spi_device->chip_select = SPI_BUS_CS;
spi_device->chip_select = SPI_MODE_0;
/* Check whether this SPI bus.cs is already claimed */
snprintf(buff, sizeof(buff), "%s.%u",
dev_name(&spi_device->master->dev),
spi_device->chip_select);
pdev = bus_find_device_by_name(spi_device->dev.bus, NULL, buff);
if (pdev) {
/* We are not going to use this spi_device, so free it */
spi_dev_put(spi_device);
/*
* There is already a device configured for this bus.cs
* It is okay if it us, otherwise complain and fail.
*/
if (pdev->driver && pdev->driver->name &&
strcmp(this_driver_name, pdev->driver->name)) {
printk(KERN_ALERT
"Driver [%s] already registered for %s\n",
pdev->driver->name, buff);
status = -1;
}
} else {
spi_device->max_speed_hz = SPI_BUS_SPEED;
/*spi_device->mode = SPI_MODE_0;*/
spi_device->mode = SPI_MODE_1;
//spi_device->bits_per_word = 32;
spi_device->irq = -1;
spi_device->controller_state = NULL;
spi_device->controller_data = NULL;
strlcpy(spi_device->modalias, this_driver_name, SPI_NAME_SIZE);
status = spi_add_device(spi_device);
if (status < 0) {
spi_dev_put(spi_device);
printk(KERN_ALERT "spi_add_device() failed: %d\n",
status);
}
}
put_device(&spi_master->dev);
printk(KERN_ALERT "QUICKLOGIC: Added device to bus, put_device called.\n");
if (gpio_request(AT91_PIN_PA24, "quicklogic_ready") < 0) {
status = -1;
printk(KERN_ALERT "QUICKLOGIC: Unable to claim GPIO pin");
}
else
gpio_direction_input(AT91_PIN_PA24);
printk(KERN_ALERT "QUICKLOGIC: Looking for Quicklogic data ready trigger on GPIO %i.\n", AT91_PIN_PA24);
printk(KERN_ALERT "QUICKLOGIC: GPIO request looks ok, trying to grab an IRQ.\n");
quicklogic_irq = gpio_to_irq(AT91_PIN_PA24); // from gpio.h
printk(KERN_ALERT "QUICKLOGIC: gpio_to_irq says we'll request IRQ %i\n", quicklogic_irq);
if (request_irq(quicklogic_irq, grab_spi_data, IRQF_TRIGGER_FALLING, "quicklogic_ready", NULL) < 0)
status = -1;
//set_irq_type(quicklogic_irq, IRQ_TYPE_EDGE_RISING);
printk(KERN_ALERT "QUICKLOGIC: IRQ %i registered\n", quicklogic_irq);
return status;
}
static struct spi_driver quicklogic_driver = {
.driver = {
.name = this_driver_name,
.owner = THIS_MODULE,
},
.probe = quicklogic_probe,
.remove = quicklogic_remove,
};
//.remove = __devexit_p(quicklogic_remove),
static int __init quicklogic_init_spi(void)
{
int error;
int i;
printk(KERN_ALERT "QUICKLOGIC: Init called, allocating memory");
/* Allocate SPI data buffers */
quicklogic_b1.tx_buff = kmalloc(4, GFP_KERNEL); // | GFP_DMA);
if (!quicklogic_b1.tx_buff) {
error = -ENOMEM;
goto quicklogic_init_error;
}
memset(quicklogic_b1.tx_buff, 0, 4);
quicklogic_b2.tx_buff = kmalloc(4, GFP_KERNEL); // | GFP_DMA);
if (!quicklogic_b2.tx_buff) {
error = -ENOMEM;
goto quicklogic_init_error;
}
memset(quicklogic_b2.tx_buff, 0, 4);
quicklogic_b1.rx_buff = kmalloc(SPI_BUFF_SIZE, GFP_KERNEL); // | GFP_DMA);
if (!quicklogic_b1.rx_buff) {
error = -ENOMEM;
goto quicklogic_init_error;
}
memset(quicklogic_b1.rx_buff, 9, SPI_BUFF_SIZE);
quicklogic_b2.rx_buff = kmalloc(SPI_BUFF_SIZE, GFP_KERNEL); // | GFP_DMA);
if (!quicklogic_b2.rx_buff) {
error = -ENOMEM;
goto quicklogic_init_error;
}
memset(quicklogic_b2.rx_buff, 9, SPI_BUFF_SIZE);
/* configure DMA recieve buffer */
/*quicklogic_b1.rx_dma = dma_map_single(&quicklogic_dev.spi_device->dev,
(void*)quicklogic_b1.rx_buff, SPI_BUFF_SIZE, DMA_FROM_DEVICE);
quicklogic_b2.rx_dma = dma_map_single(&quicklogic_dev.spi_device->dev,
(void*)quicklogic_b2.rx_buff, SPI_BUFF_SIZE, DMA_FROM_DEVICE);
quicklogic_b1.tx_dma = dma_map_single(&quicklogic_dev.spi_device->dev,
(void*)quicklogic_b1.tx_buff, 4, DMA_TO_DEVICE);
quicklogic_b2.tx_dma = dma_map_single(&quicklogic_dev.spi_device->dev,
(void*)quicklogic_b2.tx_buff, 4, DMA_TO_DEVICE);*/
printk(KERN_ALERT "QUICKLOGIC: Allocating locks\n");
/* Allocate and initialize buffer locks */
quicklogic_b1.lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL);
quicklogic_b2.lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL);
spin_lock_init(quicklogic_b1.lock);
spin_lock_init(quicklogic_b2.lock);
/* init both buffers as dirty so as not to read useless information. */
quicklogic_b1.dirty = 1;
quicklogic_b2.dirty = 1;
printk(KERN_ALERT "QUICKLOGIC: Init SPI messages\n");
/* init SPI messages */
for (i = 0; i < SPI_MSG_BUFFER; i++) {
spi_message_init(&quicklogic_b1.msg[i]);
spi_message_init(&quicklogic_b2.msg[i]);
quicklogic_b1.msg[i].status = 0;
quicklogic_b2.msg[i].status = 0;
/*Tell the driver we want DMA */
//quicklogic_b1.msg[i].is_dma_mapped = 1;
//quicklogic_b2.msg[i].is_dma_mapped = 1;
}
error = spi_register_driver(&quicklogic_driver);
if (error < 0) {
printk(KERN_ALERT "spi_register_driver() failed %d\n", error);
goto quicklogic_init_error;
}
else {
printk(KERN_ALERT "QUICKLOGIC: spi_register driver complete.\n");
}
error = add_quicklogic_device_to_bus();
if (error < 0) {
printk(KERN_ALERT "add_quicklogic_to_bus() failed\n");
spi_unregister_driver(&quicklogic_driver);
goto quicklogic_init_error;
}
else {
printk(KERN_ALERT "QUICKLOGIC: add_quicklogic_device_to_bus complete.\n");
}
return 0;
quicklogic_init_error:
if (quicklogic_b1.tx_buff) {
kfree(quicklogic_b1.tx_buff);
quicklogic_b1.tx_buff = 0;
}
if (quicklogic_b2.tx_buff) {
kfree(quicklogic_b2.tx_buff);
quicklogic_b2.tx_buff = 0;
}
if (quicklogic_b1.rx_buff) {
kfree(quicklogic_b1.rx_buff);
quicklogic_b1.rx_buff = 0;
}
if (quicklogic_b2.rx_buff) {
kfree(quicklogic_b2.rx_buff);
quicklogic_b2.rx_buff = 0;
}
return error;
}
static const struct file_operations quicklogic_fops = {
.owner = THIS_MODULE,
.read = quicklogic_read,
.open = quicklogic_open,
};
static int __init quicklogic_init_cdev(void)
{
int error;
quicklogic_dev.devt = MKDEV(0, 0);
error = alloc_chrdev_region(&quicklogic_dev.devt, 0, 1, this_driver_name);
if (error < 0) {
printk(KERN_ALERT "alloc_chrdev_region() failed: %d \n",
error);
return -1;
}
cdev_init(&quicklogic_dev.cdev, &quicklogic_fops);
quicklogic_dev.cdev.owner = THIS_MODULE;
error = cdev_add(&quicklogic_dev.cdev, quicklogic_dev.devt, 1);
if (error) {
printk(KERN_ALERT "cdev_add() failed: %d\n", error);
unregister_chrdev_region(quicklogic_dev.devt, 1);
return -1;
}
return 0;
}
static int __init quicklogic_init_class(void)
{
quicklogic_dev.class = class_create(THIS_MODULE, this_driver_name);
if (!quicklogic_dev.class) {
printk(KERN_ALERT "class_create() failed\n");
return -1;
}
if (!device_create(quicklogic_dev.class, NULL, quicklogic_dev.devt, NULL,
this_driver_name)) {
printk(KERN_ALERT "device_create(..., %s) failed\n",
this_driver_name);
class_destroy(quicklogic_dev.class);
return -1;
}
return 0;
}
static int __init quicklogic_init(void)
{
memset(&quicklogic_dev, 0, sizeof(quicklogic_dev));
memset(&quicklogic_b1, 0, sizeof(quicklogic_b1));
memset(&quicklogic_b2, 0, sizeof(quicklogic_b2));
spin_lock_init(&quicklogic_dev.spi_lock);
sema_init(&quicklogic_dev.fop_sem, 1);
if (quicklogic_init_cdev() < 0)
goto fail_1;
if (quicklogic_init_class() < 0)
goto fail_2;
if (quicklogic_init_spi() < 0)
goto fail_3;
return 0;
fail_3:
device_destroy(quicklogic_dev.class, quicklogic_dev.devt);
class_destroy(quicklogic_dev.class);
fail_2:
cdev_del(&quicklogic_dev.cdev);
unregister_chrdev_region(quicklogic_dev.devt, 1);
fail_1:
return -1;
}
module_init(quicklogic_init);
static void __exit quicklogic_exit(void)
{
spi_unregister_device(quicklogic_dev.spi_device);
spi_unregister_driver(&quicklogic_driver);
device_destroy(quicklogic_dev.class, quicklogic_dev.devt);
class_destroy(quicklogic_dev.class);
cdev_del(&quicklogic_dev.cdev);
unregister_chrdev_region(quicklogic_dev.devt, 1);
if (quicklogic_b1.tx_buff)
kfree(quicklogic_b1.tx_buff);
if (quicklogic_b2.tx_buff)
kfree(quicklogic_b2.tx_buff);
if (quicklogic_b1.rx_buff)
kfree(quicklogic_b1.rx_buff);
if (quicklogic_b2.rx_buff)
kfree(quicklogic_b2.rx_buff);
if (quicklogic_dev.user_buff)
kfree(quicklogic_dev.user_buff);
//TODO free_irq(irq_num, NULL);
gpio_free(AT91_PIN_PA24);
}
module_exit(quicklogic_exit);
MODULE_AUTHOR("Maxwell Bottiger");
MODULE_DESCRIPTION("Quicklogic interface module");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.3");