Программирование контроллера DMA Atmel

Я работаю над модулем ядра Linux для считывания данных с чипа на шине SPI системы AT91SAM9x25EK на чипе. Я нахожу какое-то необычное поведение, хотя. Чип должен возвращать наборы чисел, которые выглядят так:

170, 172, 172, 172, 170, 173, 173, 173, 170, 174, 174, 174, 170, 175, 175, 175 и т. Д.

По сути, я должен увидеть 170, а затем 3 байта, которые увеличиваются с каждым чтением.

Это прекрасно работает с PIO, но не практично. Я должен читать 4 байта каждые 200 мкс, что связывает весь процессор. Итак, DMA - это путь.

Тем не менее, у меня есть 2 проблемы с 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 снижает нагрузку на процессор до половины, но портит мои результаты. Теперь мой чип выкладывает такие вещи:

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);
}
}

0 ответов

Другие вопросы по тегам