--- /dev/null
+/*
+ sciLogger SPI driver
+ spike.c
+
+ GPIO144 DRDY signal from PIC24@CPU2010
+ SPI2 AD data receive and CMD send for PIC24@CPU2010
+
+ Copyright Naoya Takamura, 2011
+ This program based on spike.c
+---------------------------------------------
+ spike.c
+
+ Copyright 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 <asm/uaccess.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+
+#include "spike-ad.h"
+
+#define SPI_BUFF_SIZE 2048
+#define USER_BUFF_SIZE 128
+
+#define SPI_BUS 1
+#define SPI_BUS_CS0 0
+#define SPI_BUS_SPEED 500000 // Hz
+
+#define SPI_DATA_SIZE 945 // $含む
+
+#define GPIO_DRDY_IN 144 // DRDY Input = GPIO144
+
+#define DEBUG_TOGGLE_OUT // デバッグ時定義する
+#ifdef DEBUG_TOGGLE_OUT
+ #define GPIO_TOGGLE_OUT 145 // Debug用toggle出力 = GPIO145
+#endif
+
+/**** 注意! Version */
+#define VERSION "1.0"
+#define MODULE_NAME "spike-ad"
+
+const char this_driver_name[] = MODULE_NAME;
+
+struct spike_control {
+ struct spi_message msg;
+ struct spi_transfer transfer;
+ u32 busy; // 1=spi_async()終了待ち
+ u32 spi_callbacks;
+// u32 busy_counter;
+ u8 *tx_buff;
+ u8 *rx_buff;
+ int received_len;
+};
+
+static struct spike_control spike_ctl;
+
+struct irq_device {
+ struct semaphore sem;
+ int irq;
+};
+
+static struct irq_device irq_dev;
+
+
+struct spike_dev {
+ spinlock_t spi_lock;
+ struct semaphore spi_sem;
+ 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 spike_dev spike_dev;
+
+// ファイル管理領域
+typedef struct {
+ unsigned long f_version; // 識別用 f_version
+ wait_queue_head_t wait; /* read and write queues */
+ int sleep_mode; // 0: 起きてる 1: 待ち
+ int intflag; // 割り込みフラグ 1=割り込み入った
+} FileInfo;
+
+static FileInfo finfo;
+
+void spike_tasklet_func(unsigned long data);
+DECLARE_TASKLET(spike_tasklet, spike_tasklet_func, 0);
+
+//仮
+static char ring_buf[SPI_DATA_SIZE];
+
+// 送信データ
+#define SPI_TX_MAX (64)
+static unsigned char spitxbuf[SPI_TX_MAX];
+int spi_tx_len;
+
+
+/*
+ spike_queue_spi_write()で開始した
+ spi_async()終了後に呼ばれる
+*/
+static void spike_spi_completion_handler(void *arg)
+{
+ // Streaming mapping DMA unmap
+// unmap_dma_buf();
+
+ spike_ctl.spi_callbacks++;
+ spike_ctl.busy = 0;
+// 受信したデータを保存 仮
+memcpy(ring_buf, spike_ctl.rx_buff, SPI_DATA_SIZE);
+ // 実際に受信できたデータ長
+ spike_ctl.received_len = spike_ctl.msg.actual_length;
+
+ // 書き込み位置進める
+// ring_write_plus();
+
+ // 寝ているものを起こす
+ if (finfo.sleep_mode) {
+ // 待ちを
+ wake_up_interruptible(&(finfo.wait)); // 起こす
+//printk(KERN_INFO "intsel_interrupt: wakeup %ld\n", fi[i].f_version);
+ finfo.sleep_mode = 0;
+ finfo.intflag = 1;
+ }
+}
+/*
+ spi_async()で送信
+*/
+static int spike_queue_spi_write(void)
+{
+ int status;
+ unsigned long flags;
+
+ // struct spi_messageを初期化 ゼロクリア
+ spi_message_init(&spike_ctl.msg);
+
+ // Callback関数設定
+ spike_ctl.msg.complete = spike_spi_completion_handler;
+ spike_ctl.msg.context = NULL;
+
+ /* write some toggling bit patterns, doesn't really matter */
+ memset(spike_ctl.tx_buff, 0, SPI_BUFF_SIZE);
+ spike_ctl.tx_buff[0] = '$';
+ memset(spike_ctl.rx_buff, 0, SPI_BUFF_SIZE);
+
+ spike_ctl.transfer.tx_buf = spike_ctl.tx_buff;
+ spike_ctl.transfer.rx_buf = spike_ctl.rx_buff;
+ spike_ctl.transfer.len = SPI_DATA_SIZE;
+
+ spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
+
+ spin_lock_irqsave(&spike_dev.spi_lock, flags);
+
+ if (spike_dev.spi_device)
+ status = spi_async(spike_dev.spi_device, &spike_ctl.msg);
+ else
+ status = -ENODEV;
+
+ spin_unlock_irqrestore(&spike_dev.spi_lock, flags);
+
+ if (status == 0)
+ spike_ctl.busy = 1;
+
+ return status;
+}
+/*
+ Tasklet
+ IRQ Handlerで呼び出される
+*/
+void spike_tasklet_func(unsigned long data)
+{
+ int status;
+ // SPI送受信開始
+ status = spike_queue_spi_write();
+ if (status) {
+ // error
+ } else {
+ // ok
+ }
+}
+/*
+ DRDY Interrupt Handler
+*/
+static irqreturn_t irq_handler(int irq, void *dev_id)
+{
+#ifdef DEBUG_TOGGLE_OUT
+ if (gpio_get_value(GPIO_TOGGLE_OUT)) {
+ gpio_set_value(GPIO_TOGGLE_OUT, 0);
+ } else {
+ gpio_set_value(GPIO_TOGGLE_OUT, 1);
+ }
+#endif
+ // タスクレットにまかせる
+ tasklet_schedule(&spike_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t spike_file_read(struct file *filp, char __user *buff, size_t count,
+ loff_t *offp)
+{
+ size_t len;
+ ssize_t status = 0;
+
+ if (!buff)
+ return -EFAULT;
+
+ if (*offp > 0)
+ return 0;
+
+ if (down_interruptible(&spike_dev.fop_sem))
+ return -ERESTARTSYS;
+
+if (spike_ctl.busy) {
+ sprintf(spike_dev.user_buff, "spike_ctl.busy==1\n");
+ count = strlen(spike_dev.user_buff);
+ up(&spike_dev.fop_sem);
+ return count;
+} else {
+//sprintf(spike_dev.user_buff, "DMA\n");
+
+ sprintf(spike_dev.user_buff,
+ "Status: %d\nTX: %d %d %d %d\nRX: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\nCallback=%d DRDY=%d\n",
+ spike_ctl.msg.status,
+ spike_ctl.tx_buff[0], spike_ctl.tx_buff[1],
+ spike_ctl.tx_buff[2], spike_ctl.tx_buff[3],
+ spike_ctl.rx_buff[0], spike_ctl.rx_buff[1],
+ spike_ctl.rx_buff[2], spike_ctl.rx_buff[3],
+ spike_ctl.rx_buff[4], spike_ctl.rx_buff[5],
+ spike_ctl.rx_buff[6], spike_ctl.rx_buff[7],
+ spike_ctl.rx_buff[8], spike_ctl.rx_buff[9],
+ spike_ctl.rx_buff[10], spike_ctl.rx_buff[11],
+ spike_ctl.rx_buff[12], spike_ctl.rx_buff[13],
+ spike_ctl.rx_buff[14], spike_ctl.rx_buff[15],
+ spike_ctl.spi_callbacks, gpio_get_value(GPIO_DRDY_IN));
+
+}
+// status = spike_do_one_message();
+//status = spike_send_cmd();
+//status = spike_rcv_data();
+
+#if 0
+// SPI送受信開始
+status = spike_queue_spi_write();
+
+ if (status) {
+ sprintf(spike_dev.user_buff,
+ "spike_do_one_message failed : %d\n",
+ status);
+ }
+ else {
+ }
+#endif
+
+ len = strlen(spike_dev.user_buff);
+
+ if (len < count)
+ count = len;
+
+ if (copy_to_user(buff, spike_dev.user_buff, count)) {
+ printk(KERN_ALERT "spike_read(): copy_to_user() failed\n");
+ status = -EFAULT;
+ } else {
+ *offp += count;
+ status = count;
+ }
+
+ up(&spike_dev.fop_sem);
+
+ return status;
+}
+
+static int spike_file_open(struct inode *inode, struct file *filp)
+{
+ int status = 0;
+
+ printk(KERN_INFO "spike_open: (%Lu)\n", filp->f_version);
+
+ if (down_interruptible(&spike_dev.fop_sem))
+ return -ERESTARTSYS;
+
+ if (!spike_dev.user_buff) {
+ spike_dev.user_buff = kmalloc(USER_BUFF_SIZE, GFP_KERNEL);
+ if (!spike_dev.user_buff)
+ status = -ENOMEM;
+ }
+
+ if (finfo.f_version != 0) {
+ printk(KERN_INFO "spike_open: busy\n");
+ return -EBUSY;
+ }
+
+ finfo.f_version = filp->f_version;
+ finfo.sleep_mode = 0;
+ finfo.intflag = 0;
+ init_waitqueue_head(&(finfo.wait));
+ filp->private_data = (void*)&finfo; // プライベートデータに構造体ポインタ設定
+
+// ring_clear();
+
+ up(&spike_dev.fop_sem);
+
+ return status;
+}
+
+static int spike_file_close(struct inode * inode, struct file * file)
+{
+ printk(KERN_INFO "spike_close: (%Lu)\n",file->f_version);
+
+ ((FileInfo *)(file->private_data))->f_version = 0;
+ ((FileInfo *)(file->private_data))->sleep_mode = 0;
+ ((FileInfo *)(file->private_data))->intflag = 0;
+ return 0;
+}
+
+static unsigned int spike_file_poll(struct file *file, struct poll_table_struct *ptab)
+{
+//printk(KERN_INFO "spike_file_poll: (%ld)\n",file->f_version);
+ // 割り込み入っている
+ if ( ((FileInfo *)(file->private_data))->intflag ) {
+ // フラグクリア
+ ((FileInfo *)(file->private_data))->intflag = 0;
+//printk(KERN_INFO "spike_file_poll: (%ld) interrupted\n",file->f_version);
+ return POLLIN | POLLRDNORM; // 読み込みOK
+ }
+ // 割り込み入っていないので待ち行列に追加する 割り込みハンドラが起こす
+ poll_wait(file, &(((FileInfo *)(file->private_data))->wait), ptab);
+//printk(KERN_INFO "spike_file_poll: (%ld) poll_wait\n",file->f_version);
+
+ // sleep_mode=寝ている
+ ((FileInfo *)(file->private_data))->sleep_mode = 1;
+
+ return 0;
+}
+
+static long spike_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int i;
+ unsigned char *p;
+
+// printk(KERN_INFO "spike_file_ioctl: (%ld)\n",file->f_version);
+ switch(cmd) {
+ // SPI送信データ長セット
+ case CMD_TX_LEN:
+ if (copy_from_user(&spi_tx_len, (void *)arg, sizeof(int))) {
+ printk(KERN_ALERT "spike_file_ioctl(): copy_from_user() failed\n");
+ return -EFAULT;
+ }
+ if (spi_tx_len > SPI_TX_MAX) spi_tx_len = SPI_TX_MAX;
+ return 0;
+ // SPI送信データセット
+ case CMD_TX_SET:
+ if (copy_from_user(&spitxbuf, (void *)arg, spi_tx_len)) {
+ printk(KERN_ALERT "spike_file_ioctl(): copy_from_user() failed\n");
+ return -EFAULT;
+ }
+ return 0;
+ // SPI受信データ返す
+ case CMD_RX_GET:
+ // リングバッファからデータ取得
+// p = ring_get(ring_read_get());
+p = ring_buf;
+ // 読み込み位置進める
+// ring_read_plus();
+ if (copy_to_user((void *)arg, p, SPI_DATA_SIZE)) {
+ printk(KERN_ALERT "spike_file_ioctl(): copy_to_user() failed\n");
+ return -EFAULT;
+ }
+ return 0;
+ // SPIで実際に受信しているデータ長を返す
+ case CMD_RECEIVED_LEN_GET:
+ i = spike_ctl.received_len;
+ if (copy_to_user((void *)arg, &i, sizeof(int))) {
+ printk(KERN_ALERT "spike_file_ioctl(): copy_to_user() failed\n");
+ return -EFAULT;
+ }
+ return 0;
+ // リングバッファにあるデータ数を返す
+ case CMD_DNUM_GET:
+// i = ring_num_get();
+i = 1;
+ if (copy_to_user((void *)arg, &i, sizeof(int))) {
+ printk(KERN_ALERT "spike_file_ioctl(): copy_to_user() failed\n");
+ return -EFAULT;
+ }
+ return 0;
+ // リングバッファクリア
+ case CMD_BUF_CLEAR:
+// ring_clear();
+ return 0;
+ default:
+ printk(KERN_INFO "spike_file_ioctl: unknown cmd=%d\n", cmd);
+ return 0;
+ }
+}
+
+static const struct file_operations spike_fops = {
+ .owner = THIS_MODULE,
+// .read = spike_file_read,
+ .open = spike_file_open,
+ .release = spike_file_close,
+ .poll = spike_file_poll,
+ .unlocked_ioctl = spike_file_ioctl,
+};
+
+
+static int spike_probe(struct spi_device *spi_device)
+{
+ if (down_interruptible(&spike_dev.spi_sem))
+ return -EBUSY;
+
+ spike_dev.spi_device = spi_device;
+
+ up(&spike_dev.spi_sem);
+
+ return 0;
+}
+
+static int spike_remove(struct spi_device *spi_device)
+{
+ if (down_interruptible(&spike_dev.spi_sem))
+ return -EBUSY;
+
+ spike_dev.spi_device = NULL;
+
+ up(&spike_dev.spi_sem);
+
+ return 0;
+}
+/*
+ SPIデバイスの設定
+*/
+static int __init add_spike_device_to_bus(void)
+{
+ struct spi_master *spi_master;
+ struct spi_device *spi_device;
+ struct device *pdev;
+ char buff[64];
+ int status = 0;
+
+ 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_CS0;
+
+ /* 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->bits_per_word = 8;
+ 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);
+
+ return status;
+}
+
+static struct spi_driver spike_driver = {
+ .driver = {
+ .name = this_driver_name,
+ .owner = THIS_MODULE,
+ },
+ .probe = spike_probe,
+ .remove = __devexit_p(spike_remove),
+};
+
+static int __init spike_init_spi(void)
+{
+ int error;
+
+ spike_ctl.tx_buff = kmalloc(SPI_BUFF_SIZE, GFP_KERNEL | GFP_DMA);
+ if (!spike_ctl.tx_buff) {
+ error = -ENOMEM;
+ goto spike_init_error;
+ }
+
+ spike_ctl.rx_buff = kmalloc(SPI_BUFF_SIZE, GFP_KERNEL | GFP_DMA);
+ if (!spike_ctl.rx_buff) {
+ error = -ENOMEM;
+ goto spike_init_error;
+ }
+
+ error = spi_register_driver(&spike_driver);
+ if (error < 0) {
+ printk(KERN_ALERT "spi_register_driver() failed %d\n", error);
+ goto spike_init_error;
+ }
+
+ error = add_spike_device_to_bus();
+ if (error < 0) {
+ printk(KERN_ALERT "add_spike_to_bus() failed\n");
+ spi_unregister_driver(&spike_driver);
+ goto spike_init_error;
+ }
+// 一貫性のあるDMAマッピング
+/*
+spike_dev.spi_device->dev.coherent_dma_mask = 0xFFFFFFFF;
+
+spike_ctl.tx_buff = dma_alloc_coherent(&(spike_dev.spi_device->dev), SPI_BUFF_SIZE, &spike_ctl.tx_dma, GFP_ATOMIC);
+ if (!spike_ctl.tx_buff) {
+ error = -ENOMEM;
+ goto spike_init_error;
+ }
+
+// spike_ctl.rx_buff = kmalloc(SPI_BUFF_SIZE, GFP_KERNEL | GFP_DMA);
+spike_ctl.rx_buff = dma_alloc_coherent(&(spike_dev.spi_device->dev), SPI_BUFF_SIZE, &spike_ctl.rx_dma, GFP_ATOMIC);
+ if (!spike_ctl.rx_buff) {
+ error = -ENOMEM;
+ goto spike_init_error;
+ }
+*/
+
+ return 0;
+
+spike_init_error:
+
+ if (spike_ctl.tx_buff) {
+ kfree(spike_ctl.tx_buff);
+ spike_ctl.tx_buff = 0;
+//dma_free_coherent(&(spike_dev.spi_device->dev), SPI_BUFF_SIZE, spike_ctl.tx_buff, spike_ctl.tx_dma);
+//spike_ctl.tx_dma = 0;
+ }
+
+ if (spike_ctl.rx_buff) {
+ kfree(spike_ctl.rx_buff);
+ spike_ctl.rx_buff = 0;
+//dma_free_coherent(&(spike_dev.spi_device->dev), SPI_BUFF_SIZE, spike_ctl.rx_buff, spike_ctl.rx_dma);
+//spike_ctl.rx_dma = 0;
+ }
+
+ return error;
+}
+
+
+static int __init spike_init_cdev(void)
+{
+ int error;
+
+ spike_dev.devt = MKDEV(0, 0);
+
+ error = alloc_chrdev_region(&spike_dev.devt, 0, 1, this_driver_name);
+ if (error < 0) {
+ printk(KERN_ALERT "alloc_chrdev_region() failed: %d \n",
+ error);
+ return -1;
+ }
+
+ cdev_init(&spike_dev.cdev, &spike_fops);
+ spike_dev.cdev.owner = THIS_MODULE;
+
+ error = cdev_add(&spike_dev.cdev, spike_dev.devt, 1);
+ if (error) {
+ printk(KERN_ALERT "cdev_add() failed: %d\n", error);
+ unregister_chrdev_region(spike_dev.devt, 1);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int __init spike_init_class(void)
+{
+ spike_dev.class = class_create(THIS_MODULE, this_driver_name);
+
+ if (!spike_dev.class) {
+ printk(KERN_ALERT "class_create() failed\n");
+ return -1;
+ }
+
+ if (!device_create(spike_dev.class, NULL, spike_dev.devt, NULL,
+ this_driver_name)) {
+ printk(KERN_ALERT "device_create(..., %s) failed\n",
+ this_driver_name);
+ class_destroy(spike_dev.class);
+ return -1;
+ }
+
+ return 0;
+}
+int spike_init_gpio(void)
+{
+ if (gpio_request(GPIO_DRDY_IN, "GPIO_DRDY_IN")) {
+ printk(KERN_ALERT "gpio_request(GPIO_DRDY_IN) failed\n");
+ goto init_gpio_fail_1;
+ }
+
+ if (gpio_direction_input(GPIO_DRDY_IN)) {
+ printk(KERN_ALERT "gpio_direction_input(GPIO_DRDY_IN) failed\n");
+ goto init_gpio_fail_2;
+ }
+#ifdef DEBUG_TOGGLE_OUT
+ if (gpio_request(GPIO_TOGGLE_OUT, "GPIO_TOGGLE_OUT")) {
+ printk(KERN_ALERT "gpio_request(GPIO_TOGGLE_OUT) failed\n");
+ goto init_gpio_fail_2;
+ }
+
+ if (gpio_direction_output(GPIO_TOGGLE_OUT, 0)) {
+ printk(KERN_ALERT "gpio_direction_output(GPIO_TOGGLE_OUT) failed\n");
+ goto init_gpio_fail_3;
+ }
+#endif
+
+ return 0;
+
+#ifdef DEBUG_TOGGLE_OUT
+init_gpio_fail_3:
+ gpio_free(GPIO_TOGGLE_OUT);
+#endif
+
+init_gpio_fail_2:
+ gpio_free(GPIO_DRDY_IN);
+
+init_gpio_fail_1:
+
+ return -1;
+}
+void spike_free_gpio(void)
+{
+ gpio_free(GPIO_DRDY_IN);
+#ifdef DEBUG_TOGGLE_OUT
+ gpio_free(GPIO_TOGGLE_OUT);
+#endif
+}
+int spike_init_irq(void)
+{
+ int result;
+
+ irq_dev.irq = OMAP_GPIO_IRQ(GPIO_DRDY_IN);
+ result = request_irq(irq_dev.irq,
+ irq_handler,
+ IRQF_TRIGGER_FALLING,
+ "spike",
+ &irq_dev);
+
+ if (result < 0) {
+ printk(KERN_ALERT "request_irq failed: %d\n", result);
+ return -1;
+ }
+
+ return 0;
+}
+void spike_free_irq(void)
+{
+ free_irq(irq_dev.irq, &irq_dev);
+}
+
+static int __init spike_init(void)
+{
+ memset(&spike_dev, 0, sizeof(spike_dev));
+ memset(&spike_ctl, 0, sizeof(spike_ctl));
+
+ sema_init(&spike_dev.spi_sem, 1);
+ sema_init(&spike_dev.fop_sem, 1);
+ spin_lock_init(&spike_dev.spi_lock);
+
+ memset(&irq_dev, 0, sizeof(irq_dev));
+ sema_init(&irq_dev.sem, 1);
+
+ finfo.f_version = 0; // 未使用マーク
+ spi_tx_len = 0;
+ // リングバッファ初期化
+// ring_init();
+
+ if (spike_init_cdev() < 0)
+ goto fail_1;
+
+ if (spike_init_class() < 0)
+ goto fail_2;
+
+ if (spike_init_spi() < 0)
+ goto fail_3;
+ // DRDY GPIO144 Input config
+ if (spike_init_gpio() < 0)
+ goto fail_4;
+#if 1
+ // DRDY GPIO144 Interrupt config
+ if (spike_init_irq() < 0)
+ goto fail_5;
+#endif
+
+ printk(KERN_INFO "%s %s initialized\n", MODULE_NAME, VERSION);
+ return 0;
+
+fail_5:
+ spike_free_gpio();
+
+fail_4:
+ if (spike_ctl.tx_buff)
+ kfree(spike_ctl.tx_buff);
+ if (spike_ctl.rx_buff)
+ kfree(spike_ctl.rx_buff);
+
+fail_3:
+ device_destroy(spike_dev.class, spike_dev.devt);
+ class_destroy(spike_dev.class);
+
+fail_2:
+ cdev_del(&spike_dev.cdev);
+ unregister_chrdev_region(spike_dev.devt, 1);
+
+fail_1:
+ return -1;
+}
+module_init(spike_init);
+
+static void __exit spike_exit(void)
+{
+ spi_unregister_device(spike_dev.spi_device);
+ spi_unregister_driver(&spike_driver);
+
+#if 1
+ spike_free_irq();
+#endif
+ spike_free_gpio();
+
+ device_destroy(spike_dev.class, spike_dev.devt);
+ class_destroy(spike_dev.class);
+
+ cdev_del(&spike_dev.cdev);
+ unregister_chrdev_region(spike_dev.devt, 1);
+
+ if (spike_ctl.tx_buff)
+ kfree(spike_ctl.tx_buff);
+//dma_free_coherent(&(spike_dev.spi_device->dev), SPI_BUFF_SIZE, spike_ctl.tx_buff, spike_ctl.tx_dma);
+
+ if (spike_ctl.rx_buff)
+ kfree(spike_ctl.rx_buff);
+//dma_free_coherent(&(spike_dev.spi_device->dev), SPI_BUFF_SIZE, spike_ctl.rx_buff, spike_ctl.rx_dma);
+
+ if (spike_dev.user_buff)
+ kfree(spike_dev.user_buff);
+
+ printk(KERN_INFO "%s %s removed\n", MODULE_NAME, VERSION);
+}
+module_exit(spike_exit);
+
+MODULE_AUTHOR("Naoya Takamura");
+MODULE_DESCRIPTION("sciLog AD SPI driver based on spike");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+