Skip to content

Latest commit

 

History

History
157 lines (128 loc) · 5.43 KB

File metadata and controls

157 lines (128 loc) · 5.43 KB

DMA

DMA工作原理及Linux下DMA实际使用

参考文档

DMA工作原理

一个DMA controller可以"同时"进行的DMA传输的个数是有限的,这称作DMA channels。这里的channel,只是一个逻辑概念,因为鉴于总线访问的冲突,以及内存一致性的考量,从物理的角度看,不大可能会同时进行两个(及以上)的DMA传输。

因而DMA channel不太可能是物理上独立的通道;很多时候,DMA channels是DMA controller为了方便,抽象出来的概念,让consumer以为独占了一个channel,实际上所有channel的DMA传输请求都会在DMA controller中进行仲裁,进而串行传输;

因此,软件也可以基于controller提供的channel(我们称为"物理"channel),自行抽象更多的“逻辑”channel,软件会管理这些逻辑channel上的传输请求。实际上很多平台都这样做了,在DMA Engine framework中,不会区分这两种channel(本质上没区别)。

dts

apdma: dma-controller@11000580 {
        compatible = "mediatek,mt6577-uart-dma";
        reg = <0 0x11000680 0 0x80>,
              <0 0x11000700 0 0x80>,
              <0 0x11000780 0 0x80>,
              <0 0x11000800 0 0x80>;
        interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_LOW>,
                     <GIC_SPI 54 IRQ_TYPE_LEVEL_LOW>,
                     <GIC_SPI 55 IRQ_TYPE_LEVEL_LOW>,
                     <GIC_SPI 60 IRQ_TYPE_LEVEL_LOW>;
        clocks = <&infracfg_ao CLK_IFR_AP_DMA>;
        clock-names = "apdma";
        #dma-cells = <1>;
};

uart0: serial@11020000 {
        compatible = "mediatek,mt6761-uart",
                     "mediatek,mt6577-uart";
        reg = <0 0x11002000 0 0x1000>;
        interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_LOW>;
        clocks = <&clk26m>, <&infracfg_ao CLK_IFR_UART0>;
        clock-names = "baud", "bus";
        dmas = <&apdma 0
                &apdma 1>;
        dma-names = "tx", "rx";
};

uart1: serial@11030000 {
        compatible = "mediatek,mt6761-uart",
                     "mediatek,mt6577-uart";
        reg = <0 0x11003000 0 0x1000>;
        interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_LOW>;
        clocks = <&clk26m>, <&infracfg_ao CLK_IFR_UART1>;
        clock-names = "baud", "bus";
        dmas = <&apdma 2
                &apdma 3>;
        dma-names = "tx", "rx";
};

DMA Controller

  • kernel-4.9/drivers/dma/8250_mtk_dma.c
    static int mtk_dma_probe(struct platform_device *pdev)
    {
            // ...省略
    
            for (i = 0; i < MTK_SDMA_CHANNELS; i++) {
                    res = platform_get_resource(pdev, IORESOURCE_MEM, i);
                    if (res == NULL)
                            return -ENODEV;
                    mtkd->mem_base[i] = devm_ioremap_resource(&pdev->dev, res);
                    if (IS_ERR(mtkd->mem_base[i]))
                            return PTR_ERR(mtkd->mem_base[i]);
            }
    
            /* request irq */
            for (i = 0; i < MTK_SDMA_CHANNELS; i++) {
                    mtkd->dma_irq[i] = platform_get_irq(pdev, i);
                    if ((int)mtkd->dma_irq[i] < 0) {
                            pr_err("Cannot claim IRQ%d\n", i);
                            return mtkd->dma_irq[i];
                    }
            }
    
            // ...省略
    }

DMA Consumer

  • kernel-4.9/drivers/tty/serial/8250/8250_mtk.c
    static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
                               struct mtk8250_data *data)
    {
            // ...省略
    
            data->dma = NULL;
    #ifdef CONFIG_SERIAL_8250_DMA
            dmacnt = of_property_count_strings(pdev->dev.of_node, "dma-names");
            if (dmacnt == 2) {
                    data->dma = devm_kzalloc(&pdev->dev, sizeof(*(data->dma)), GFP_KERNEL);
                    data->dma->fn = mtk8250_dma_filter;
                    data->dma->rx_size = MTK_UART_RX_SIZE;
                    data->dma->rxconf.src_maxburst = MTK_UART_RX_TRIGGER;
                    data->dma->txconf.dst_maxburst = MTK_UART_TX_TRIGGER;
            }
    #endif
    
            return 0;
    }
    #endif
    
    static int mtk8250_probe(struct platform_device *pdev)
    {
            // ...省略
    
    #ifndef CONFIG_FPGA_EARLY_PORTING
            if (pdev->dev.of_node) {
                    err = mtk8250_probe_of(pdev, &uart.port, data);
                    if (err)
                            return err;
            } else
                    return -ENODEV;
    #endif
    
            spin_lock_init(&uart.port.lock);
            uart.port.mapbase = regs->start;
            uart.port.irq = irq->start;
            uart.port.pm = mtk8250_do_pm;
            uart.port.type = PORT_16550;
            uart.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT;
            uart.port.dev = &pdev->dev;
            uart.port.iotype = UPIO_MEM32;
            uart.port.regshift = 2;
            uart.port.private_data = data;
            uart.port.shutdown = mtk8250_shutdown;
            uart.port.startup = mtk8250_startup;                      // DMA设备树配置的Channel获取
            uart.port.set_termios = mtk8250_set_termios;              // 串口开启的时候,会调用到这里面的DMA使能
            uart.port.uartclk = clk_get_rate(data->uart_clk);
    #ifdef CONFIG_FPGA_EARLY_PORTING
            uart.port.uartclk = MTK_UART_FPGA_CLK;
    #endif
    #ifdef CONFIG_SERIAL_8250_DMA
            if (data->dma)
                    uart.dma = data->dma;
    #endif
    
            // ...省略
    }