2

AT91SAM9x25EK システム オン チップの SPI バス上のチップからカウントを読み取る Linux カーネル モジュールに取り組んでいます。しかし、私はいくつかの異常な動作を見つけています。チップは、次のような数値のセットを返す必要があります。

170、172、172、172、170、173、173、173、170、174、174、174、170、175、175、175など

基本的に、170 の後に 3 バイトが表示され、読み取りごとに増加します。

これは PIO では問題なく機能しますが、実用的ではありません。200us ごとに 4 バイトを読み取る必要があり、CPU 全体が拘束されます。そのため、DMA が最適です。

ただし、DMA には 2 つの問題があります。最初は 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 を設定する私の init 関数です。

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 転送を設定する私の関数です。基本的に、大量の転送セットを 1 つのメッセージにキューイングします。

#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