mini2440 spi驱动总结

dannistang 2011-04-08

最近在参考了很多的网络资源后编写调试了在mini2440开发板上的SPI驱动程序,因为急于使用也没有分析S3C2440在Linux下自带的源程序,编写程序的方式较为简单,只是实现了简单的字符发送和接收。

主要是配置一些寄存器。这些寄存器的配置按照S3C2440手册的说明来进行设置,根据mini2440开发板只有SPI0可以使用,SPI1被按键使用了。采用GPE和GPG来进行控制。

SPI 的编程步骤是当一个字节数据写入SPTDATn寄存器,如果ENSCK、SPCONn寄存器的MSTR被置位,SPI开始发送,可以采用以下不住来操作SPI:手册上的编程基本步骤:

(1)时钟波特率预定标器寄存器(SPPREn)
(2)设置SPCONn来合理配合SPI模块
(3)写数据0XFF到SPTDATn到SPTDATn 10次,目的是初始化MMC或SD卡
(4)设置一个GPTO引脚作为nSS,低电平是激活MMC或SD卡
(5)发送数据->检查传输准备标志(REDY=1)的状态,然后写数据到SPTDATn
(6)接收数据(1):SPCONn的TAGD位是无效=normal mode(0)->写0XFF 到SPTDATn,然后确认REDY置1,然后从读缓存读取数据
(7)接收数据(2):SPCONn的TAGD位是有效=Tx Auto Garbage Data mode->确认REDY置1,然后从读缓存读取数据(然后自动开始传输)
(8)设置一个GPIO引脚,其作为nSS,高电平是解除激活MMC或SD卡。
源代码如下,具体分析等以后有时间补上。这个程序在mini2440下直接编译即可运行。我在网上搜索的没有可以直接运行,也许是我分析的不够透彻,因为时间紧迫仅仅看了下大体的结构和自己需要的部分。希望大家多多给写意见和改进措施。
#include <linux/irq.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/major.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>//printk()
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>//u8,u16,u3……
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/sched.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>//copy_to_user(),copy_from_user()
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <linux/spinlock.h>
#include <asm/system.h>
#include <asm/io.h>
//--------------------------------------------------------------------------------------------------------------
#define SPI_NAME "MINI2440_SPI"
static int SPI_MAJOR= 55;
//static int spi_major=0;//主设备号为0表示动态分配主设备号,自定义次设备号。MKDEV()表示主次合并为设备号
struct spi_dev
{
 struct cdev cdev;
 char dataTx[4];
 char dataRx[4];
};
struct spi_dev *spi_devp;
//-------------------------------------------------------------------------------------------------------
//GPG and GPE control register setting
#define GPG_DEFAULT (S3C2410_GPG3_EINT11|S3C2410_GPG6_EINT14|S3C2410_GPG2_nSS0)
#define GPE_DEFAULT (S3C2410_GPE11_SPIMISO0|S3C2410_GPE12_SPIMOSI0|S3C2410_GPE13_SPICLK0)
#define SPI_CON_DEFAULT (0<<0|0<<1|1<<2|1<<3|1<<4|0<<5|0<<6)//具体设置根据情况确定
#define SPI_TXRX_READY ((readl(SPSTA0)&0x1)==0x1)//发送接收标志位判断
//-------------------------------------------------------------------------------------------------------
//GPG  GPE SPI address declare
//GPG
#define GPGCON (unsigned long)ioremap(0x56000060,4)
#define GPGDAT (unsigned long)ioremap(0x56000064,4)
#define GPGUP  (unsigned long)ioremap(0x56000068,4)
//GPE
#define GPECON (unsigned long)ioremap(0x56000040,4)
#define GPEDAT (unsigned long)ioremap(0x56000044,4)
#define GPEUP  (unsigned long)ioremap(0x56000048,4)
//SPI
#define SPCON0  (unsigned long)ioremap(0x59000000,4)
#define SPSTA0  (unsigned long)ioremap(0x59000004,4)
#define SPPIN0  (unsigned long)ioremap(0x59000008,4)
#define SPPRE0  (unsigned long)ioremap(0x5900000c,4)
#define SPTDAT0 (unsigned long)ioremap(0x59000010,1)
#define SPRDAT0 (unsigned long)ioremap(0x59000014,1)
//CLK control
#define CLKCON (unsigned long)ioremap(0x4c00000c,4)
#define SCRPND  (unsigned long)ioremap(0x4a000000,4)
#define INTPND  (unsigned long)ioremap(0x4a000010,4)
#define INTMSK  (unsigned long)ioremap(0x4a000008,4)
//----------------------------------------------------------------------------------------------------------------------------
//mini2440 SPI config
static void mini2440_spi_config(void)
{
 unsigned int port_status;
 if(!(CLKCON&(1<<18)))
 {
  port_status=readl(CLKCON);
  port_status|=(1<<18);
  writel(port_status,CLKCON);//时钟使能
 }
 
 port_status=readl(GPGCON);
 port_status&=~GPG_DEFAULT;
 port_status|=GPG_DEFAULT;//GPG control
 writel(port_status,GPGCON);

 port_status=readl(GPECON);
 port_status&=~GPE_DEFAULT;
 port_status|=GPE_DEFAULT;
 writel(port_status,GPECON);//GPE control
 
 port_status=0x0;
 port_status=readl(GPEUP);
 port_status&=~(1<<13|1<<12|1<<11);
 writel(port_status,GPEUP);//GPE_UP

 port_status=0x0;
 port_status=readl(GPGUP);
 port_status&=~(1<<2|1<<3|1<<6);
 writel(port_status,GPGUP);//GPG_UP
 
 //disable_irq(IRQ_SPI0);
 port_status=readl(SPPRE0);//SPI Baud speed
 port_status=0x31;//PCLK=50MHz SPICLK=PCLK/2/(value+1)=1MHz,对于CC2500最大支持500K,一般采用250K
 writel(port_status,SPPRE0);

 port_status=readl(SPCON0);
 port_status=SPI_CON_DEFAULT;
 writel(port_status,SPCON0);//SPI control

 
 port_status=readl(SPPIN0);
 port_status&=~(0<<0|1<<1|1<<2);
 port_status|=(1<<2|1<<1|0<<0);
 writel(port_status,SPPIN0);//SPI pin setting

 port_status=readl(SCRPND);
 port_status|=(1<<22);
 writel(port_status,SCRPND);
 
 port_status=readl(INTPND);
 port_status|=(1<<22);
 writel(port_status,INTPND);

 //enable_irq(IRQ_SPI0);
}
//-----------------------------------------------------------------------------------------------------------------
static int spi_open(struct inode *inode,struct file *filp)
{
 filp->private_data=spi_devp;
 mini2440_spi_config();
 //filep->private_data=spi_devp;
 s3c2410_gpio_setpin(S3C2410_GPG(2),0);
 printk("-----------------------------------\n");
 printk("MINI2440 SPI GPG GPE CLK set over\n");
 printk("-----------------------------------\n");
 return 0;
}

static int spi_release(struct inode *inode,struct file *filp)
{
 printk("-----------------------------\n");
 printk("S3C2440-mini2440_spi closed\n");
 printk("------------------------------\n");
 return 0;
}
//-----------------------------------------------------------------------------------------------------------
static ssize_t spi_read(struct file *filp,char __user *buf,size_t count)
{
 //char *spiRxData;
 struct spi_dev *dev=filp->private_data;
 copy_to_user(buf,dev->dataRx,count);
 return 0;
}

static ssize_t spi_write(struct file *filp,char __user *buf,size_t count)
{
 volatile int endSpiTx=0;
 volatile char spiTxData[4];
 //int config;
 int i=0;
 //char str[20];
 //char *txStr,*rxStr;
 //volatile char *spiTxStr,*spiRxStr;
 unsigned char string;
 unsigned int port_status;
 struct spi_dev *dev=filp->private_data;
 //filp->private_data;
 //获得设备结构体的指针
 copy_from_user(dev->dataTx,buf,count);
 for(i=0;i<4;i++)
 {
  spiTxData[i]=dev->dataTx[i];
 }
 printk("receive form user is %s\n",spiTxData);
 while(endSpiTx==0)
 {
  //if(*spiTxData!='\0')
  while(!SPI_TXRX_READY)
   printk("SPI TXRX unready\n"); 
  for(i=0;i<4;i++)
  {
     
   //s3c2410_gpio_setpin(S3C2410_GPG(2),0);//使能从SPI  
  
   writeb(spiTxData[i],SPTDAT0);//=*(spiTxData++);
   string=readb(SPTDAT0);
   printk("transfer char=%c\n",string);
   //while(!SPI_TXRX_READY);
   dev->dataRx[i]=readb(SPRDAT0);
   printk("----------------------------\n");  
   printk("receive char=%c\n",dev->dataRx[i]);
   printk("----------------------------\n");
   //s3c2410_gpio_setpin(S3C2410_GPG(2),1);//解除使能从SPI
   //i++;
  }
  endSpiTx=1;
 }
 port_status=readl(SPCON0);
 port_status|=((0<<5)|(0<<4)|(1<<3)|(1<<2)|(0<<1)|(0<<0));
 writel(port_status,SPCON0);
 return 0;
}
//---------------------------------------------------------------------------------------------------------------
static const struct file_operations spi_fops={
 .owner=THIS_MODULE,
 .open=spi_open,
 .release=spi_release,
 .read=spi_read,
 .write=spi_write,
};
static void spi_interrupt(int irq,void *dev_id,struct pt_regs *reg)
{
 unsigned int port_status;
 unsigned int i=0;
 volatile int endSpiRx=0;

 port_status=readl(SCRPND);
 port_status|=(1<<22);
 writel(port_status,SCRPND);
 
 port_status=readl(INTPND);
 port_status|=(1<<22);
 writel(port_status,INTPND);
 //SCRPND|=(1<<22);
 //INTPND|=(1<<22);

 disable_irq(IRQ_SPI0);
// SPCON0|=(0<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2)|(0<<1)|(1<<0);
 
 
 
 while(endSpiRx==0)
 {
  while (!SPI_TXRX_READY);
  writeb(0xff,SPTDAT0);
  while(!SPI_TXRX_READY);
  spi_devp->dataRx[i]=SPRDAT0;
  i++;
  if(i>4)
  {
   endSpiRx=1;
  }
 }
 port_status=readl(SPCON0);
 port_status|=((0<<6)|(1<<5)|(0<<4)|(1<<3)|(1<<2)|(0<<1)|(0<<0));
 writel(port_status,SPCON0);
 
 enable_irq(IRQ_SPI0);
 printk("SPI irq end\n");
}
static void spi_setup_cdev(struct spi_dev *dev,int index)
{
 int err,devno=MKDEV(SPI_MAJOR,index);
 cdev_init(&dev->cdev,&spi_fops);
 //cdev_init(&spi_dev,&spi_fops);
 dev->cdev.owner=THIS_MODULE;
 dev->cdev.ops=&spi_fops;
 err=cdev_add(&dev->cdev,devno,1);
 if(err)
 printk("error\n");
 printk("-----------------------------------\n");
}
static int __init spi_init(void)
{
 int ret;
 dev_t devno=MKDEV(SPI_MAJOR,0);
 if(SPI_MAJOR)//申请设备号
  ret=register_chrdev_region(devno,1,SPI_NAME);
 else//动态申请设备号
 {
  ret=alloc_chrdev_region(&devno,0,1,SPI_NAME);
  SPI_MAJOR=MAJOR(devno);
 }
 if(ret<0)
  return ret;
 //动态申请设备结构体的内存
 spi_devp=kmalloc(sizeof(struct spi_dev),GFP_KERNEL);
 if(!spi_devp)//申请失败
 {
  ret=-ENOMEM;
  goto fail_malloc;
 }
 memset(spi_devp,0,sizeof(struct spi_dev));
 
 spi_setup_cdev(spi_devp,0);
 
 printk("init spi success\n");
 return 0;
fail_malloc:
 unregister_chrdev_region(devno,1);
 return ret;
}
static void __exit spi_exit(void)
{
 free_irq(IRQ_SPI0,NULL);
 unregister_chrdev(SPI_MAJOR,SPI_NAME);
}
//-----------------------------------------------------------------------------------------------------------------------
module_init(spi_init);
module_exit(spi_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gflytu");
MODULE_DESCRIPTION("SPI DRIVER FOR MINI_S3C2440");

相关推荐