DMA

论坛 期权论坛 脚本     
已经匿名di用户   2022-5-29 19:15   2062   0

一、框架

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

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:81
帖子:4969
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP