DMA enables peripherals to transfer data directly between memory and hardware registers without CPU intervention. In Linux, DMA operations replace CPU-driven data writes to device registers, such as SPI_DATA for SPI communication, shifting from writel(data, SPI_DATA) to automated buffer transfers.
Key considerations include address handling: CPUs use virtual addresses, while DMA operates on physical addresses. Essentially, DMA controllers move data between specified physical memory locations.
Implementing DMA in Linux involves these primary steps:
DMA Initialization
dma_cap_mask_t capabilities;
dma_cap_zero(capabilities);
dma_cap_set(DMA_SLAVE, capabilities);
struct dma_chan *rx_channel = dma_request_channel(capabilities, filter_func, ¶ms);
The dma_request_channel() function allocates a DMA channel, using a filter callback to match the requesting device's DMA signals based on bus registrasion data.
void *buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
struct scatterlist sg;
sg_init_one(&sg, buffer, BUFFER_SIZE);
Memory allocation for DMA data buffers is followed by scatterlist initialization to manage virtual-to-physical address mappings.
DMA Trensfer Execution
struct dma_async_tx_descriptor *descriptor;
struct dma_chan *channel;
struct dma_slave_config configuration;
channel = controller->tx_channel;
configuration.direction = DMA_MEM_TO_DEV;
configuration.dst_addr = controller->hw_addr;
configuration.dst_maxburst = MAX_BURST_16;
configuration.dst_addr_width = DMA_SLAVE_WIDTH_16BIT;
channel->device->device_control(channel, DMA_SLAVE_CONFIG, (unsigned long)&configuration);
controller->transfer_length = data_len;
dma_map_sg(device, &sg, 1, DMA_TO_DEVICE);
descriptor = channel->device->device_prep_slave_sg(channel,
&sg,
1,
DMA_MEM_TO_DEV,
DMA_INTERRUPT | DMA_NO_DEST_UNMAP);
descriptor->callback = transfer_complete;
descriptor->callback_param = &callback_data;
dmaengine_submit(descriptor);
dma_async_issue_pending(channel);
Configuration specifies transfer direction, destination address, burst size, and data width. Address mapping is estalbished via dma_map_sg(), followed by descriptor preparation with completion callback registration. The transfer initiates with dma_async_issue_pending().