DMA的工作流程
對於linux中的DMA,其實是在寫數據寄存器的時候就可以用dma的傳輸來代替。就像spi設備,在發送和接收數據的時候都是要往數據寄存器中寫數據的。比如那個寄存器是SPI_DATA,如果用cpu來傳輸的話就是writel(data, SPI_DATA);而用dma傳輸就是配置好要傳輸的buf長度,然后源地址就是buf的地址,目標地址就是SPI_DATA。
這里還要注意經過cpu的是虛擬地址,而dma傳輸的是物理地址。
其實dma傳輸就是dma控制在兩個物理地址之間傳輸數據。
Linux下用dma傳輸主要調用下面這些函數就可以實現外部的dma,下圖為流程圖
1、初始化DMA
1 dma_cap_zero(mask); 2 dma_cap_set(DMA_SLAVE,mask); 3 4 /*1. Init rx channel */ 5 dws->rxchan= dma_request_channel(mask, dma_chan_filter, params);// 主要就是申請DMA通道。dma_chan_filter這個函數主要是查找你的dma傳輸的設備的請求信號線,其具體是在注冊時填寫的。 這里會根據這個函數返回的真假來判斷已經注冊在總線上的dma slave的。
6 buf =kmalloc(DMA_BUFFER_SIZE, GFP_KERNEL); //申請一塊地址,用來DMA傳輸的數據就放在這里
7 sg_init_one(&dma_dev->dmatx.sg, buf, DMA_BUFFER_SIZE); //初始化,其主要為了發送時虛擬地址和物理地址的映射。
2、啟動DMA
1 struct dma_async_tx_descriptor *txdesc = NULL; 2 struct dma_chan *txchan,; 3 struct dma_slave_config txconf; 4 5 txchan= dws->txchan; 6 7 /*2. Prepare the TX dma transfer */ 8 txconf.direction= DMA_TO_DEVICE; //表示dma傳輸方向為發送 9 txconf.dst_addr= dws->dma_addr; //目標地址,物理地址 10 txconf.dst_maxburst= LNW_DMA_MSIZE_16; //最大傳輸的字節數 11 txconf.dst_addr_width= DMA_SLAVE_BUSWIDTH_2_BYTES; //數據的位寬 12 13 txchan->device->device_control(txchan,DMA_SLAVE_CONFIG, 14 (unsigned long) &txconf); 15 16 dws->tx_sgl.length= dws->len; //要傳輸的數據的長度 17 18 dma_map_sg(dma_dev->dev,&dmatx->sg, 1, DMA_TO_DEVICE); 19 //通過這個函數來實現虛擬地址和物理地址的映射。 20 21 txdesc= txchan->device->device_prep_slave_sg(txchan, 22 &dws->tx_sgl, 23 1, 24 DMA_TO_DEVICE, 25 DMA_PREP_INTERRUPT| DMA_COMPL_SKIP_DEST_UNMAP); 26 txdesc->callback= dw_spi_dma_done; //傳輸完成后的回調函數 27 txdesc->callback_param= params; //回調函數中的參數 28 29 dmaengine_submit(txdesc);
30 dma_dev->device_issue_pending(txchan); // 啟動dma傳輸了