DMA

一、框架

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 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值