Программирование контроллера 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);
}
}