一、框架

provider注册dma
pl330_probe(struct amba_device *adev, const struct amba_id *id)
{
struct dma_device *pd;
填充该结构体
list_add_tail(&pch->chan.device_node, &pd->channels);
pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
pd->device_free_chan_resources = pl330_free_chan_resources;
pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
pd->device_tx_status = pl330_tx_status;
pd->device_prep_slave_sg = pl330_prep_slave_sg;
pd->device_control = pl330_control;
pd->device_issue_pending = pl330_issue_pending;
#ifdef CONFIG_ARCH_ROCKCHIP
pd->dma_getposition = pl330_dma_getposition;
#endif
ret = dma_async_device_register(pd);
}
consumer接口
1. dts:
uart0: serial@20060000 {
compatible = "rockchip,serial";
reg = <0x20060000 0x100>;
interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
clock-frequency = <594000000>;
clocks = <&clk_uart0>, <&clk_gates8 0>;
clock-names = "sclk_uart", "pclk_uart";
reg-shift = <2>;
reg-io-width = <4>;
dmas = <&pdma 2>, <&pdma 3>;---------->通过pdma设备node找到具体的provider
#dma-cells = <2>;
pinctrl-names = "default";
pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>;
status = "disabled";
};
2. 申请DMA channel
struct dma_chan *dma_request_chan(struct device *dev, const char *name);
3. 配置DMA channel的参数
int dmaengine_slave_config(struct dma_chan *chan, struct dma_slave_config *config)
4. 获取传输描述(tx descriptor)根据用于场景
struct dma_async_tx_descriptor *dmaengine_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_data_direction direction, unsigned long flags); struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_data_direction direction); struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma( struct dma_chan *chan, struct dma_interleaved_template *xt, unsigned long flags); |
5.启动传输
dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc) void dma_async_issue_pending(struct dma_chan *chan); |

3. 代码举例说明
static int serial_rk_init_dma_rx(struct uart_rk_port *up)
{
struct dma_slave_config slave_config;
//dts:dma-names = "tx", "rx";也可没有
uart_dma->dma_chan_rx = dma_request_slave_channel(port->dev, "rx");
slave_config.direction = DMA_DEV_TO_MEM;
slave_config.src_addr = port->mapbase + UART_RX;
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
slave_config.src_maxburst = 1;-------->有的dma自带缓冲区,当缓冲区levl达到时,一次性拷贝到
内存buf
//为啥此处没有设置dst?因为这个是通道channel的配置,只涉及设备端的地址,所以若是接收,则设备端为数据源,需设置src,而mem端地址是在获取描述符中设置的
ret = dmaengine_slave_config(uart_dma->dma_chan_rx, &slave_config);
}
static int serial_rk_start_dma_rx(struct uart_rk_port *up)
{
struct dma_async_tx_descriptor *desc;
serial_rk_init_dma_rx(up)) -------》上面讲述的channle配置
desc = dmaengine_prep_dma_infiniteloop(uart_dma->dma_chan_rx, uart_dma->rx_phy_addr,
uart_dma->rb_size, uart_dma->rb_size / 2, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT, 2);
//本质调用的是
return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len,
period_len, dir, flags, &t);
//注意:buf_add为mem地址,buf_len:内存长度,period_len:dma中断周期,buf_len为period_len整数倍,例如pingpang buf模式
/* desc->callback = dma_rx_callback; */ ?为啥没有设置回调函数 用户唤醒取出数据
dmaengine_submit(desc);
dma_async_issue_pending(uart_dma->dma_chan_rx);
}
dma 设置mem地址,那地址从哪来的呢?
两种途径:
1. dma驱动自己分配;
dma_addr_t dma_handle; 物理地址
cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, gfp);----》返回值:虚拟地址
2. 其他模块申请内存,来映射到物理层
streaming DMA mapping的接口函数可以在中断上下文中调用。streaming DMA mapping有两个版本的接口函数,一个是用来map/umap单个的dma buffer,另外一个是用来map/umap形成scatterlist的多个dma buffer。
struct device *dev = &my_dev->dev;
dma_addr_t dma_handle;
void *addr = buffer->ptr;----------》虚拟地址
size_t size = buffer->len;---------》长度
dma_handle = dma_map_single(dev, addr, size, direction);-------》返回值为物理地址,该映射会进行cash的跟新
if (dma_mapping_error(dev, dma_handle)) {
goto map_error_handling;
}
还有一种dma_map_sg
这两种用时:每次都需要map-->umap 这里面主要对coach的跟新。
3. 总结两种:第一种调用一次后,不用每次重新map,而第二次每次传输完成都需要重新map
|