S3C2440驱动篇—Linux平台设备驱动之SPI

这两天参考网上的资料,自己写了个SPI的驱动,并实际测试通过。

 

硬件平台:mini2440 用的是S3C2440 的SPI1(共有2个SPI模块)

 

操作系统:linux-2.6.32.2

 

测试方法:将SPI的MISO与MOSI管脚短路,这样读数据的时候第一个发出的dummy字节即为收到的字节。

 

下面是驱动的源代码(mini2440_spi.c):

 

 

 

/***************************************************/

#include <linux/irq.h>

#include <linux/miscdevice.h>

#include <linux/delay.h>

#include <asm/irq.h>

#include <linux/interrupt.h>

#include <mach/regs-gpio.h>

#include <mach/hardware.h>

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/mm.h>

#include <linux/fs.h>

#include <linux/types.h>

#include <linux/delay.h>

#include <linux/moduleparam.h>

#include <linux/slab.h>

#include <linux/errno.h>

#include <linux/ioctl.h>

#include <linux/cdev.h>

#include <linux/string.h>

#include <linux/list.h>

#include <linux/pci.h>

#include <linux/gpio.h>

#include <asm/uaccess.h>

#include <asm/atomic.h>

#include <asm/unistd.h>

#include <linux/spinlock.h>

#include <asm/system.h>

#include <asm/uaccess.h>

 

int loopChar=0x88;

module_param(loopChar,int,S_IRUGO);

                                                   

static int spi_major = 55;

#define spi_name "mini2440_spi"

struct cdev spiCdev;

 

/*****************************************************/

 

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);

 

volatile int *spi_gpfcon=NULL;//GPF Part define

volatile int *spi_gpfdat=NULL;

volatile int *spi_gpfup=NULL;

 

volatile int *spi_gpgcon=NULL;//GPG Part define

volatile int *spi_gpgdat=NULL;

volatile int *spi_gpgup=NULL;

 

volatile int *s3c2440_clkcon;

volatile int *spi_spcon1;//SPI Part define

volatile int *spi_spsta1;

volatile int *spi_sppin1;

volatile int *spi_sppre1;

volatile int *spi_sptdat1;

volatile int *spi_sprdat1;

 

#define SPI_TXRX_READY        (((*spi_spsta1)&0x1) == 0x1)

/**********************************************************/

static const struct file_operations spi_fops =

{

       .owner=THIS_MODULE,

       .open=spi_open,

       .read=spi_read,

       .ioctl=spi_ioctl,

       .release=spi_release,

       .write=spi_write,

};

 

/********************************************************/

static int spi_open(struct inode *inode,struct file *filp)

{

       filp->private_data =&spiCdev;     

       /***********************************************

       *PCLK

       ************************************************/

       /*control PCLK into spi block*/

       *s3c2440_clkcon |=0x40000;

       printk("s3c2440_clkcon=%08X\n",*s3c2440_clkcon);

       /***********************************************

       *GPG PORTS

       ************************************************/

       /*config SCK1,MOSI1,MISO1 = 11*/

       *spi_gpgcon |=0x0000FC00;

       /*poll up MISO1 MOSI1,SCK1*/

       *spi_gpgup &=0xFF1F;

       *spi_gpgup |=0x0060;

       /***********************************************

       *GPF PORTS

       ************************************************/

       *spi_gpfcon &= 0xFCF3;

       *spi_gpfcon |= 0x0108; 

       *spi_gpfup &= 0xED;

       *spi_gpfdat |= 0x10;

       /***********************************************

       *SPI REGS

       ************************************************/

       //SPI Baud Rate Prescaler Register,Baud Rate=PCLK/2/(Prescaler value+1)

       *spi_sppre1=0x18;        //freq = 1M

       printk("spi_sppre1=%02X\n",*spi_sppre1);

       //polling,en-sck,master,low,format A,nomal = 0 | TAGD = 1

       *spi_spcon1=(0<<6)|(0<<5)|(1<<4)|(1<<3)|(0<<2)|(0<<1)|(0<<0);

       printk("spi_spcon1=%02X\n",*spi_spcon1);

       //Multi Master error detect disable,reserved,rech=*spi_sprdat0;lease

       *spi_sppin1=(0<<2)|(0<<0);

       printk("spi_sppin1=%02X\n",*spi_sppin1);

 

       return 0;

}

 

 

static int spi_release(struct inode *inode,struct file *filp)

{

       //free irq

       free_irq(IRQ_EINT1, NULL);

 

       printk("<1>release\n");

       return 0;

}

 

 

static void writeByte(const char c)

{

       int j = 0;

       *spi_sptdat1 = c;

       for(j=0;j<0xFF;j++);

       while(!SPI_TXRX_READY)

              for(j=0;j<0xFF;j++);

}

 

static char readByte(void)

{

       int j = 0;

       char ch = 0;

       *spi_sptdat1 = (char)loopChar;

       for(j=0;j<0xFF;j++);

       while(!SPI_TXRX_READY)

              for(j=0;j<0xFF;j++);

       ch=*spi_sprdat1;

       return ch;

}

static ssize_t spi_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops)

{

       //int len=0;

       char ch;

       printk("<1>spi read!\n");

       ch=readByte();

       copy_to_user(buf,&ch,1);

       return 1;

}

 

static ssize_t spi_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_ops)

{

       int i;

       char *kbuf;

       printk("<1>spi write!,count=%d\n",count);

       kbuf=kmalloc(count,GFP_KERNEL);

       if(copy_from_user(kbuf,buf,count))

       {

              printk("no enough memory!\n");

              return -1;

       }

      

       for(i=0;i<count;i++)

       {

              writeByte(*kbuf);

              printk("write 0x%02X!\n",*kbuf);

              kbuf++;

       }

       return count;

}

 

static ssize_t spi_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data)

{

       return 0;

}

 

 

static int __init spi_init(void)

{

       int result;

      dev_t devno = MKDEV(spi_major, 0);

 

      /**/

       if (spi_major)

              result = register_chrdev_region(devno, 1, spi_name);

       else  /**/

       {

              result = alloc_chrdev_region(&devno, 0, 1, spi_name);

              spi_major = MAJOR(devno);

       } 

      if (result < 0)

           return result;

          

    cdev_init(&spiCdev, &spi_fops);

      spiCdev.owner = THIS_MODULE;

       spiCdev.ops = &spi_fops;

       if (cdev_add(&spiCdev, devno, 1))

              printk(KERN_NOTICE "Error adding spi %d", 0);

 

       s3c2440_clkcon = (int *)ioremap(0x4C00000c,3);

 

       spi_gpgcon = (int *)ioremap (0x56000060,4);

       spi_gpgdat = (int *)ioremap (0x56000064,2);

       spi_gpgup = (int *)ioremap (0x56000068,2);

 

       spi_gpfcon = (int *)ioremap (0x56000050,2);

       spi_gpfdat = (int *)ioremap (0x56000054,1);

       spi_gpfup = (int *)ioremap (0x56000058,1);

 

       spi_spcon1 = (int *)ioremap(0x59000020,1);

       spi_spsta1 = (int *)ioremap(0x59000024,1);

       spi_sppin1 = (int *)ioremap(0x59000028,1);

       spi_sppre1 = (int *)ioremap(0x5900002c,1);

       spi_sptdat1 = (int *)ioremap(0x59000030,1);

       spi_sprdat1 = (int *)ioremap(0x59000034,1);

 

 

       printk("Init spi success!\n"); 

       return result;

}

 

 

static void __exit spi_exit(void)

{

       cdev_del(&spiCdev);

       unregister_chrdev_region(MKDEV(spi_major, 0), 1);

       printk("<1>spi_exit!\n");

}

 

module_init(spi_init);

module_exit(spi_exit);

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("nkzc");

MODULE_DESCRIPTION("SPI driver for S3C2440");

 

 

 

 

几点需要注意的地方:

 

1.一开始在spi_exit()函数中使用了void unregister_chrdev(unsigned int major, const char *name)函数来注销设备,但再次insmod驱动的时候提示"Device or resource busy",改为unregister_chrdev_region()后一切正常,说明即使只注册了一个设备,register_chrdev_region()和unregister_chrdev_region()也要配套使用。

 

2.定义spi_spcon1等寄存器变量时前面要加上volatile关键字,这样每次访问该变量时cpu会从实际内存中读取该值而不是使用寄存器中的值。尤其是spi_spsta1变量,它的最低位代表了spi发送接收是否ready,如果没有volatile,可能会在readByte()或writeByte()函数中导致死循环。

 

3.使用了module_param()宏向驱动传递参数,这里定义了一个int型的loopChar参数,加载模块时使用insmod mini2440_spi.ko loopChar=123 来设置loopChar的值。

 

 

 

测试程序:spi_test.c

 

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

 

int main(int argc, char **argv)

{

       int fd;

       int count=0;

       char buf[]={0x11,0x22,0x33,0x44,0x55};

       fd = open("/dev/mini2440_spi", O_RDWR);

       if (fd < 0) {

              perror("open device spi");

              exit(1);

       }

       count=write(fd,buf,sizeof(buf)/sizeof(buf[0]));

       read(fd,buf,1);

       printf("read byte is: 0x%02X\n",buf[0]);

       close(fd);

       return 0;

}

 

很简单的一个程序,分别调用了open,write,read,close函数,可以观察输出结果,验证驱动程序是否正确,read的输出即为loopChar的值。

 

注意:open的时候要注意第二个参数flag,只有当flag为O_RDWR时,驱动中的相应的spi_read,spi_write函数才会被调用。

 

 

 

你可能感兴趣的:(c,linux,struct,Module,File,平台)