C Atmel DMA控制器编程

C Atmel DMA控制器编程,c,linux,driver,dma,spi,C,Linux,Driver,Dma,Spi,我正在开发一个Linux内核模块,从芯片上的AT91SAM9x25EK系统的SPI总线上的芯片读取计数。不过我发现了一些不寻常的行为。芯片应返回如下所示的数字集: 170、172、172、172、170、173、173、170、174、174、174、170、175、175等 基本上,我应该看到170后跟3个字节,它们随着每次读取而增加 这对于PIO来说很好,但并不实用。我必须每200us读取4个字节,这会占用整个CPU。因此,DMA是一条出路 但是,DMA有两个问题。第一个来自atmel_sp

我正在开发一个Linux内核模块,从芯片上的AT91SAM9x25EK系统的SPI总线上的芯片读取计数。不过我发现了一些不寻常的行为。芯片应返回如下所示的数字集:

170、172、172、172、170、173、173、170、174、174、174、170、175、175等

基本上,我应该看到170后跟3个字节,它们随着每次读取而增加

这对于PIO来说很好,但并不实用。我必须每200us读取4个字节,这会占用整个CPU。因此,DMA是一条出路

但是,DMA有两个问题。第一个来自atmel_spi.c,文件顶部显示:

#if defined(CONFIG_SPI_ATMEL_DMA)
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
 * cache operations; better heuristics consider wordsize and bitrate.
 */
#define DMA_MIN_BYTES   16
显然,我需要的4个字节小于DMA的最低要求。这意味着要么我必须从芯片上一次读取16个字节,要么将这个#define改为4。无论哪种情况,结果都是一样的。激活DMA控制器将CPU使用率降低到一半以下,但会弄糟我的结果。现在,我的芯片吐出这样的东西:

170,0,0,0,170,0,0,0,170,0,0,0,0,170,0,0,0,0

我的170标记仍在那里,但所有其他读数返回0。我是否读取了正确的字节数似乎并不重要,每次我使用DMA控制器时,每个值都会得到0,我完全被难倒了

如果有人知道出了什么问题,或者有人知道我如何完全绕过DMA的整个问题,我会很兴奋地尝试一下。200us是一个很难满足的要求,但在实现的其余部分,我可能有更多的喘息空间

这是我的init函数,用于设置内核缓冲区和DMA:

static int __init quicklogic_init_spi(void)
{
int error;

quicklogic_ctl.tx_buff = kmalloc(4, GFP_KERNEL | GFP_DMA);
if (!quicklogic_ctl.tx_buff) {
    error = -ENOMEM;
    goto quicklogic_init_error;
}
memset(quicklogic_ctl.tx_buff, 0, 4);

quicklogic_ctl.rx_buff = kmalloc(SPI_BUFF_SIZE, GFP_KERNEL | GFP_DMA);
if (!quicklogic_ctl.rx_buff) {
    error = -ENOMEM;
    goto quicklogic_init_error;
}
memset(quicklogic_ctl.rx_buff, 9, SPI_BUFF_SIZE);

/* configure DMA recieve buffer */
quicklogic_ctl.rx_dma = dma_map_single(&quicklogic_dev.spi_device->dev,
    (void*)quicklogic_ctl.rx_buff, SPI_BUFF_SIZE, DMA_FROM_DEVICE);
quicklogic_ctl.tx_dma = dma_map_single(&quicklogic_dev.spi_device->dev,
    (void*)quicklogic_ctl.tx_buff, 4, DMA_TO_DEVICE);
/*Tell the driver we want DMA */ 
quicklogic_ctl.msg.is_dma_mapped = 1;
error = spi_register_driver(&quicklogic_driver);
if (error < 0) {
    printk(KERN_ALERT "spi_register_driver() failed %d\n", error);
    goto quicklogic_init_error;
}

error = add_quicklogic_device_to_bus();
if (error < 0) {
    printk(KERN_ALERT "add_quicklogic_to_bus() failed\n");
    spi_unregister_driver(&quicklogic_driver);
    goto quicklogic_init_error; 
}

quicklogic_prepare_spi_message();
/*my messages are always the same, so set this up only once*/

return 0;

 quicklogic_init_error:

if (quicklogic_ctl.tx_buff) {
    kfree(quicklogic_ctl.tx_buff);
    quicklogic_ctl.tx_buff = 0;
}

if (quicklogic_ctl.rx_buff) {
    kfree(quicklogic_ctl.rx_buff);
    quicklogic_ctl.rx_buff = 0;
}

return error;
}
static int\uu init quicklogic\u init\u spi(void)
{
整数误差;
quicklogic_ctl.tx_buff=kmalloc(4,GFP_内核| GFP_DMA);
如果(!quicklogic_ctl.tx_buff){
error=-ENOMEM;
转到quicklogic_init_错误;
}
memset(quicklogic_ctl.tx_buff,0,4);
quicklogic_ctl.rx_buff=kmalloc(SPI_buff_大小,GFP_内核| GFP_DMA);
如果(!quicklogic\u ctl.rx\u buff){
error=-ENOMEM;
转到quicklogic_init_错误;
}
memset(quicklogic_ctl.rx_buff,9,SPI_buff_大小);
/*配置DMA接收缓冲区*/
quicklogic_ctl.rx_dma=dma_map_single(&quicklogic_dev.spi_device->dev,
(void*)quicklogic_ctl.rx_buff、SPI_buff_大小、来自_设备的DMA_);
quicklogic_ctl.tx_dma=dma_map_single(&quicklogic_dev.spi_device->dev,
(void*)quicklogic_ctl.tx_buff,4,DMA_至_设备);
/*告诉司机我们想要DMA*/
quicklogic_ctl.msg.is_dma_mapped=1;
错误=spi_寄存器_驱动程序(&quicklogic_驱动程序);
如果(错误<0){
printk(内核警报“spi\U寄存器\U驱动程序()失败%d\n”,错误);
转到quicklogic_init_错误;
}
错误=将_快速逻辑_设备_添加到_总线();
如果(错误<0){
printk(内核警报“将快速逻辑添加到总线()失败\n”);
spi_注销_驱动程序(&quicklogic_驱动程序);
转到quicklogic_init_错误;
}
quicklogic_prepare_spi_message();
/*我的消息总是一样的,所以只设置一次*/
返回0;
quicklogic_init_错误:
if(快速逻辑控制发送缓冲){
kfree(quicklogic_ctl.tx_buff);
quicklogic_ctl.tx_buff=0;
}
if(快速逻辑控制增益){
kfree(快速逻辑控制增益);
quicklogic_ctl.rx_buff=0;
}
返回误差;
}
另外,这是我设置SPI传输的函数。本质上,我将一大组传输排队到一条消息中

#define SPI_TRANSFERS   150528
#define SPI_MSG_LEN     4
#define SPI_MSG_DELAY   200 /*in microseconds*/
#define SPI_BUFF_SIZE   602112 /*can hold 150528 four byte transfers (30 seconds)*/
#define USER_BUFF_SIZE  602112

const char this_driver_name[] = "quicklogic";


struct quicklogic_control {
struct spi_message msg;
struct spi_transfer transfer[SPI_TRANSFERS];
u8 *tx_buff; 
u8 *rx_buff;
dma_addr_t tx_dma;
dma_addr_t rx_dma;
};

static struct quicklogic_control quicklogic_ctl;


struct quicklogic_dev {
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 quicklogic_dev quicklogic_dev;


static void quicklogic_prepare_spi_message(void)
{
int i;
spi_message_init(&quicklogic_ctl.msg);

for (i=0; i<SPI_TRANSFERS; i++) {
    quicklogic_ctl.transfer[i].tx_buf = quicklogic_ctl.tx_buff;
    quicklogic_ctl.transfer[i].rx_buf = quicklogic_ctl.rx_buff + (i * SPI_MSG_LEN);
    quicklogic_ctl.transfer[i].len = SPI_MSG_LEN;
    quicklogic_ctl.transfer[i].delay_usecs = SPI_MSG_DELAY;

    spi_message_add_tail(&quicklogic_ctl.transfer[i], &quicklogic_ctl.msg);
}
}
#定义SPI_传输150528
#定义SPI_MSG_LEN 4
#定义SPI_MSG_延迟200/*以微秒为单位*/
#定义SPI_BUFF_大小602112/*可容纳150528个四字节传输(30秒)*/
#定义用户\u BUFF\u大小602112
const char this_driver_name[]=“quicklogic”;
结构quicklogic\u控件{
结构spi_消息消息消息;
结构spi_传输传输[spi_传输];
u8*tx\U buff;
u8*rx_buff;
dma_addr_t tx_dma;
dma地址接收dma;
};
静态结构quicklogic\u控制quicklogic\u ctl;
结构quicklogic\u dev{
结构信号量spi_sem;
结构信号量fop_sem;
发展发展;
结构cdev-cdev;
结构类*类;
结构spi_设备*spi_设备;
字符*user_buff;
u8测试数据;
};
静态结构quicklogic\u dev quicklogic\u dev;
静态无效快速逻辑\u准备\u spi\u消息(无效)
{
int i;
spi_message_init(&quicklogic_ctl.msg);

对于(i=0;我想你应该问这个问题嗨,你解决了你的问题吗?你修改了dts吗?怎么做?我确实解决了这个问题。这是很久以前的事了,但我很确定答案是坚持使用PIO模式。即使PIO传输速度较慢,也值得避免DMA开销。我认为我的高CPU使用率是由于返回BUFF如果你对最终的代码感兴趣,我可以发布它。另外,我很抱歉在将近一年后回复了你的评论。