Сбой процесса SPI в Linux в драйвере ядра
У меня есть драйвер устройства SPI (он же ведомый SPI), который я использовал в течение нескольких лет на процессоре Atmel AT91. В настоящее время я портирую драйвер на новый набор плат и перехожу на процессор Gumstix Duovero. Все старое было запущено в Linux 3.8, новое - 3.18.21 из репозитория ядра gumstix.
Короче говоря, система работает так, что есть FPGA, которая посылает импульс на вывод GPIO, чтобы сообщить процессору, что данные готовы. В ответ процессор отправляет 4 байта и читает 4 байта. Тактовая частота шины SPI установлена на 1 МГц, а сигнал FPGA, называемый "готовность к данным", срабатывает со скоростью около 20 кГц. Все передачи происходят в PIO, потому что они слишком малы для прямого доступа к памяти.
Я отлаживал новую систему в течение нескольких дней, и я на грани остроумия. Я думаю, что частота, с которой происходят прерывания, является сутью моей проблемы, но я не уверен. Я чувствую, что, возможно, обработчик прерываний срабатывает до завершения инициализации драйвера, но я могу ошибаться. В любом случае, когда вы загружаете систему, происходит одна из 3 вещей:
- Система зависает с паникой ядра. (90% времени)
- Система загружается, но spi4 умирает. (7% времени)
- Все загружается и работает как положено (3% времени)
Я не знаю огромного количества об общих интерфейсах SPI ядра и не нашел ничего, что указывало бы на проблему. Старый код напрямую называл часть SPI-кода Atmel, но я думаю, что даже сейчас все это объединено в более общие файлы spi.h и gpio.h. Глядя на трассировку ниже, очевидно, есть проблема с указателем 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)
quicklogic.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");