2

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

170、172、172、172、170、173、173、173、170、174、174、174、170、175、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, 170, 0, 0, 0

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

如果有人知道出了什么问题,或者知道如何完全规避整个 DMA 问题,我会很高兴尝试一下。200us 是一个硬性要求,但在其余的实现中我可能有更多的喘息空间。

这是我设置内核缓冲区和 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;
}

此外,这是我设置 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);
}
}
4

0 回答 0