由于我的产品,需要tiny210的SPI作为从设备,而linux下的SPI子系统只能为主设备,所以这段时间一直在写tiny210的SPI驱动,昨天才写好的,采用中断接收。在数据量小、且比较慢的情况下接收到的数据正确,但是速度快了或者数据量多了,就会出现丢数据,经过调试,发现应该是SPI进中断服务程序的时间太长了了,导致了FIFO里的数据被新来的数据所覆盖。 所以小弟想采用DMA方式。由于时间有限,希望版主或者各位大虾,指点一下PL330(DMA),如何使用。 只需要有个大概的流程就行。谢谢了!!!下面是我的SPI驱动,写得比较简单。
/********************************************
*说明:本实验是针对tiny210的SPI程序 *
*设备模型:混杂设备 *
*内核选取:linux-3.0.8 *
*写作时间:2013/6/17 *
*编辑作者:xiangrui kong *
********************************************/
#include <linux/interrupt.h>//request_irq
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <linux/ioctl.h>
#include <mach/irqs.h>
#include <mach/regs-irq.h>
#include <linux/sched.h>
#define DEVICE_NAME "tiny210_spi"
static void __iomem *base_addr0;
static void __iomem *base_addr1;
static void __iomem *base_addr2;
#define s5pv210_CLK 0xE010046C
#define s5pv210_GPB 0xE0200040
#define s5pv210_SPI 0xE1300000
/*****************************************************/
//S3C2440_CLKCON 部分
#define CLK_GATE_IP3 (*(volatile unsigned long *)(base_addr0 + 0x00))
//GPG 控制寄存器部分 GPB
#define GPBCON (*(volatile unsigned long *)(base_addr1 + 0x00))
#define GPBDAT (*(volatile unsigned long *)(base_addr1 + 0x04))
#define GPBUP (*(volatile unsigned long *)(base_addr1 + 0x08))
//SPI 控制寄存器部分
#define CH_CFG0 (*(volatile unsigned long *)(base_addr2 + 0x00))
#define CLK_CFG0 (*(volatile unsigned long *)(base_addr2 + 0x04))
#define MODE_CFG0 (*(volatile unsigned long *)(base_addr2 + 0x08))
#define CS_REG0 (*(volatile unsigned long *)(base_addr2 + 0x0c))
#define SPI_INT_EN0 (*(volatile unsigned long *)(base_addr2 + 0x10))
#define SPI_STATUS0 (*(volatile unsigned long *)(base_addr2 + 0x14))
#define SPI_TX_DATA0 (*(volatile unsigned long *)(base_addr2 + 0x18))
#define SPI_RX_DATA0 (*(volatile unsigned long *)(base_addr2 + 0x1c))
#define PACKET_CNT_REG0 (*(volatile unsigned long *)(base_addr2 + 0x20))
#define SWAP_CONFIG0 (*(volatile unsigned long *)(base_addr2 + 0x28))
#define SPI_TXRX_ON() (CH_CFG0 |= 0x3)
#define SPI_TX_DONE (((SPI_STATUS >> 21) & 0x1) == 0x1)
#define SPI_RX_num ((SPI_STATUS >>13) & 127)
#define SPI_CS_LOW() (SLAVE_SEL &= ~0x01)
#define SPI_CS_HIGH() (SLAVE_SEL |= 0x01)
char buff[250];
volatile char total_buf[1000000];
volatile long data_num = 0;
static int spi_open(struct inode *inode, struct file *filp)
{
//使能时钟控制寄存器CLKCON 18位使能SPI
CLK_GATE_IP3 |= 1 << 12;
//设置GPIO
GPBCON &= 0XFFFF0000;
GPBCON |= 0X00002222;
//GPEUP 设置;全部disable
GPBUP &= ~(0xff << 0);
//GPBDAT 设置位0
GPBDAT &= ~(0xff << 0);
//CPHA = 1;CPOL=1;为从模式
CH_CFG0 = ((1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0));
//时钟设为PCLK
CLK_CFG0 &= 0;
CLK_CFG0 |= (1<<8);
//设置SPI模式
MODE_CFG0 = ((0 << 29) | (0 << 19) | (0 << 17) | (25 << 11) | (25 << 5) | (0 << 2) | (0 << 1) | (0 << 0)); //fifo 接收数据100字节,就产生中断
CS_REG0 = (0x03);
SPI_INT_EN0 |= (1 << 1);//打开RXFIFO中断
SWAP_CONFIG0 = ((0 << 4) | (1 << 5) | (0 << 0) | (1 << 1));
return 0;
}
static ssize_t spi_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops)
{
while(count > data_num); //等待要读的数据
memcpy(buf,total_buf,count);
data_num -= count;
memcpy(total_buf,total_buf+count,data_num);
printk("data_num:%d,%d \n",data_num,count);
return 1;
}
static int spi_release(struct inode *inode, struct file *filp)
{
return 0;
}
static const struct file_operations spi_fops =
{
.owner = THIS_MODULE,
.open = spi_open,
.release = spi_release,
.read = spi_read,
};
static struct miscdevice misc =
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &spi_fops,
};
static void spi_interrupt(int irq,void *dev_id,struct pt_regs *reg)
{
int i=0;
int tmp = ((SPI_STATUS0 >> 15) & 0x1ff);
for(i=0;i<tmp;i++)
{
buff = SPI_RX_DATA0;
}
memcpy(total_buf+data_num,buff,tmp);
data_num += tmp;
printk("\n\n %d , %d , %d\n\n",((SPI_STATUS0 >> 15) & 0x1ff),tmp,data_num);
return IRQ_HANDLED;
}
static int __init spi_init(void)
{
int ret;
//寄存器映射到内存地址中。
base_addr0 = ioremap(s5pv210_CLK, 0x04);
base_addr1 = ioremap(s5pv210_GPB, 0x10);
base_addr2 = ioremap(s5pv210_SPI, 0x30);
//复杂设备的注册
ret = misc_register(&misc);
request_irq(IRQ_SPI0,spi_interrupt,IRQF_DISABLED,DEVICE_NAME,NULL);
printk(DEVICE_NAME "\tinitialized\n");
return ret;
}
static void __exit spi_exit(void)
{
iounmap(base_addr0);
iounmap(base_addr1);
iounmap(base_addr2);
misc_deregister(&misc);
printk("<1>spi_exit!\n");
}
module_init(spi_init);
如果有朋友,能帮我直接改了,就更是感谢!!
module_exit(spi_exit);
MODULE_LICENSE("GPL");