#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#define DEVICE_NAME "tq2440_spi"
#define DBG(msg...) do{ \
module_param(spi_major,int,S_IRUGO);
static struct class *spi_class;
static struct semaphore sem;
static int spi_open(struct inode *,struct file *);
static int spi_release(struct inode *,struct file *);
static ssize_t spi_write(struct file *filp,const char *buf,size_t count,loff_t *f_ops);
static ssize_t spi_read(struct file *filp,char *buf,size_t count,loff_t *f_ops);
static ssize_t spi_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data);
static void config_write(void);
static void config_read(void);
static spi_buff_t output_buff;
static spi_buff_t input_buff;
volatile unsigned long *s3c2440_clkcon;
volatile unsigned long *spi_spcon1;//SPI Part define
volatile unsigned long *spi_spsta1;
volatile unsigned long *spi_sppin1;
volatile unsigned long *spi_sppre1;
volatile unsigned long *spi_sptdat1;
volatile unsigned long *spi_sprdat1;
static struct s3c2410_dma_client spi_dma_client={
static const struct file_operations spi_fops =
static void spi_clear_buf(spi_buff_t *s)
s3c2410_dma_ctrl(DMACH_SPI1,S3C2410_DMAOP_FLUSH);
dma_free_coherent(NULL,s->size,s->start,s->dma_addr);
static int spi_open(struct inode *inode,struct file *filp)
if(down_interruptible(&sem))
filp->private_data =&spiCdev;
static int spi_release(struct inode *inode,struct file *filp)
static int spi_setup_buf(spi_buff_t *s)
dmabuf=dma_alloc_coherent(NULL,dmasize,&dmaphys,GFP_KERNEL|GFP_DMA);
DBG("buf start %p dma %d\n",s->start,s->dma_addr);
printk(KERN_ERR "unable to allocate spi menmory\n");
static ssize_t spi_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops)
spi_buff_t *s=&input_buff;
s3c2410_dma_enqueue(DMACH_SPI1,(void *)s,s->dma_addr,s->size);
if(copy_to_user(buf,s->start,s->size)){
printk("copy_to_user error\n");
static ssize_t spi_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_ops)
spi_buff_t *s=&output_buff;
DBG("spi_write:start count=%d\n",count);
if(down_interruptible(&s->sem)){
printk("down interruptible error\n");
if(copy_from_user(s->start,buf,count)){
printk("copy_from_user error\n");
if((ret=s3c2410_dma_enqueue(DMACH_SPI1,(void*)s,s->dma_addr,s->size))){
printk("dma enqueue failed.\n");
static ssize_t spi_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data)
static void spi_dmain_done_callback(struct s3c2410_dma_chan *ch,void *buf,int size,
enum s3c2410_dma_buffresult result)
spi_buff_t *b=(spi_buff_t *)buf;
DBG("spi_dmain_done_callback\n");
static void spi_dmaout_done_callback(struct s3c2410_dma_chan *ch,void *buf,int size,
enum s3c2410_dma_buffresult result)
spi_buff_t *b=(spi_buff_t *)buf;
DBG("spi_dmaout_done_callback\n");
static void config_write(void)
enum s3c2410_dmasrc source;
source=S3C2410_DMASRC_MEM;
flags=S3C2410_DMAF_AUTOSTART;
s3c2410_dma_devconfig(DMACH_SPI1, source, hwcfg, devaddr);
s3c2410_dma_config(DMACH_SPI1, 1, dcon);
s3c2410_dma_set_buffdone_fn(DMACH_SPI1, spi_dmaout_done_callback);
s3c2410_dma_setflags(DMACH_SPI1, flags);
static void config_read(void)
enum s3c2410_dmasrc source;
DBG("spi config_read\n");
source=S3C2410_DMASRC_HW;
flags=S3C2410_DMAF_AUTOSTART;
s3c2410_dma_devconfig(DMACH_SPI1, source, hwcfg, devaddr);
s3c2410_dma_config(DMACH_SPI1, 1, dcon);
s3c2410_dma_set_buffdone_fn(DMACH_SPI1, spi_dmain_done_callback);
s3c2410_dma_setflags(DMACH_SPI1, flags);
static int spi_init_dma(spi_buff_t *s,char *desc)
ret=!s3c2410_dma_request(DMACH_SPI1,&spi_dma_client,NULL);
printk(KERN_ERR "failed to get dma channel\n");
static int spi_clear_dma(spi_buff_t *s,struct s3c2410_dma_client *client)
s3c2410_dma_set_buffdone_fn(DMACH_SPI1,NULL);
s3c2410_dma_free(DMACH_SPI1,client);
static void config_spi(void)
s3c2410_gpio_cfgpin(S3C2410_GPG5,S3C2410_GPG5_SPIMISO1);
s3c2410_gpio_cfgpin(S3C2410_GPG6,S3C2410_GPG6_SPIMOSI1);
s3c2410_gpio_cfgpin(S3C2410_GPG7,S3C2410_GPG7_SPICLK1);
s3c2410_gpio_cfgpin(S3C2410_GPG3,S3C2410_GPG3_nSS1);
s3c2410_gpio_pullup(S3C2410_GPG5,1);
s3c2410_gpio_pullup(S3C2410_GPG6,1);
s3c2410_gpio_pullup(S3C2410_GPG7,1);
*s3c2440_clkcon |=0x40000;
if(spi_init_dma(&output_buff,"spi out")){
spi_clear_dma(&output_buff,&spi_dma_client);
printk(KERN_ERR "SPI_OUT"":unable to get DMA channel\n");
spi_class=class_create(THIS_MODULE,DEVICE_NAME);
printk("Err:failed in spi class.\n");
device_create(spi_class,NULL,devno,NULL,DEVICE_NAME);
printk("Init spi success!\n");
static void __exit spi_exit(void)
unregister_chrdev_region(MKDEV(spi_major, 0), 1);
device_destroy(spi_class ,MKDEV(spi_major,0));
class_destroy(spi_class);
spi_clear_dma(&output_buff,&spi_dma_client);
MODULE_DESCRIPTION("SPI driver for S3C2440");