Linux中,驱动必然会有驱动对应的设备类型。在linux4.4版本中,其设备是以设备树的形式展现的。
PS端设备树的devicetree表示如下
324
dmac_s: dmac@f8003000 {
325
compatible = "arm,pl330", "arm,primecell";
326
reg = <0xf8003000 0x1000>;
327
interrupt-parent = ;
328
interrupt-names = "abort", "dma0", "dma1", "dma2", "dma3",
329
"dma4", "dma5", "dma6", "dma7";
330
interrupts = <0 13 4>,
331
<0 14 4>, <0 15 4>,
332
<0 16 4>, <0 17 4>,
333
<0 40 4>, <0 41 4>,
334
<0 42 4>, <0 43 4>;
335
#dma-cells = <1>;
336
#dma-channels = <8>;
337
#dma-requests = <4>;
338
clocks = ;
339
clock-names = "apb_pclk";
340
};
这个文件根据设备树信息创建设备信息,在驱动程序注册时就可以找到该设备信息,执行probe函数。
zynq下dma的设备channel如下:
root@linaro-ubuntu-desktop:/sys/class/dma# ls
dma0chan0 dma0chan2 dma0chan4 dma0chan6 dma1chan0
dma0chan1 dma0chan3 dma0chan5 dma0chan7
root@linaro-ubuntu-desktop:/sys/class/dma# ll
total 0
drwxr-xr-x 2 root root 0 1970-01-01 00:00 ./
drwxr-xr-x 50 root root 0 1970-01-01 00:00 ../
lrwxrwxrwx 1 root root 0 1970-01-01 00:00 dma0chan0 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan0/
lrwxrwxrwx 1 root root 0 1970-01-01 00:00 dma0chan1 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan1/
lrwxrwxrwx 1 root root 0 1970-01-01 00:00 dma0chan2 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan2/
lrwxrwxrwx 1 root root 0 1970-01-01 00:00 dma0chan3 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan3/
lrwxrwxrwx 1 root root 0 1970-01-01 00:00 dma0chan4 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan4/
lrwxrwxrwx 1 root root 0 1970-01-01 00:00 dma0chan5 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan5/
lrwxrwxrwx 1 root root 0 1970-01-01 00:00 dma0chan6 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan6/
lrwxrwxrwx 1 root root 0 1970-01-01 00:00 dma0chan7 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan7/
lrwxrwxrwx 1 root root 0 1970-01-01 00:00 dma1chan0 -> ../../devices/soc0/fpga-axi@0/43000000.axivdma/dma/dma1chan0/
dma1chan0是xilinx AXI-VDMA IP生成的DMA控制器,其处于PL端,而dma0相关的控制器是ps端的pl330.本篇看pl330这个驱动程序的注册。
查看物理地址分布
root@linaro-ubuntu-desktop:~# cat /proc/iomem
00000000-1fffffff : System RAM
00008000-006651a3 : Kernel code
006a2000-00700867 : Kernel data
41600000-4160ffff : /fpga-axi@0/i2c@41600000
43000000-43000fff : /fpga-axi@0/axivdma@43000000
70e00000-70e0ffff : /fpga-axi@0/axi_hdmi@70e00000
75c00000-75c00fff : /fpga-axi@0/axi-spdif-tx@0x75c00000
77600000-77600fff : /fpga-axi@0/axi-i2s@0x77600000
79000000-7900ffff : /fpga-axi@0/axi-clkgen@79000000
e0001000-e0001fff : xuartps
e0002000-e0002fff : /amba/usb@e0002000
e0002000-e0002fff : /amba/usb@e0002000
e000a000-e000afff : /amba/gpio@e000a000
e000b000-e000bfff : /amba/eth@e000b000
e000d000-e000dfff : /amba/spi@e000d000
e0100000-e0100fff : mmc0
f8003000-f8003fff : /amba/dmac@f8003000
f8003000-f8003fff : /amba/dmac@f8003000
f8005000-f8005fff : /amba/watchdog@f8005000
f8007000-f80070ff : /amba/devcfg@f8007000
f8007100-f800711f : /amba/adc@f8007100
f800c000-f800cfff : /amba/ocmc@f800c000
fffc0000-ffffffff : f800c000.ocmc
root@linaro-ubuntu-desktop:~#
ARM采用统一编址,其访问内存和外设的指令是一样没有差异的,linux内核并不通过物理地址直接访问外设,而是通过虚拟地址,虚拟地址经过MMU转换成物理访问外设。所以外设需要使用ioremap()对将物理地址空间转换成虚拟地址。
I/O设备使用第三种地址,总线地址。如果一个设备在MMIO(memory mapped IO内存映射地址空间)有寄存器,或者其对系统存储系统执行DMA读写操作,设备使用的就是总线地址。
在系统枚举阶段,内核知道I/O设备以及他们的MMIO空间。如果一个设备支持DMA方式,驱动通过kmalloc()申请一段内存空间,返回申请空间的首地址,设为X,虚拟地址系统将虚拟地址X映射到物理地址Y。驱动程序可以使用虚拟地址X访问设备地址空间,但是设备本身却不行,这是因为DMA本身并不是通过虚拟地址方式来工作的。
在zynq7000设备里,DMA可以直接操作物理地址,但另一些处理器使用IOMMU将DMA地址转换物理地址,linux建议使用DMA API而不是特定总线的DMA API,比如使用dma_map_*()接口而不是pci_map_*()接口,在zynq7000中,pl330 DMA已经实现了直接调用通用的DMA框架API即可以。
DMA开发相关API
首先得包含
#include
其提供了dma_addr_t定义,其可以作为设备使用DMA的源地址或者目的地址,并且可以使用DMA_XXX相关的API。
1.使用大DMA 一致性buffer