问:就是怎么在driver设计中 pci总线上这么多设备怎么区分彼此的?区分彼此开来 ?使用次设备号吗,但是cpu怎么区分对应的pci设备
答:cpu和pci设备之间隔着pci总线控制器或者pci桥
cpu的内存地址空间和pci总线地址空间是相互独立的隔离的,只是大多数cpu架构(x86)都是简单地把这2种地址不加任何偏移地一一映射。但是驱动程序员这个概念要分清。
cpu首先要访问pci控制器,pci控制器访问pci设备
pci设备有3种空间:配置空间、IO空间、mem空间,所以要区分每个pci设备就需要完全区分这3种空间。
其中mem空间就是pci设备分得的pci总线地址,每一个pci设备分得的pci地址空间是不会重叠的。cpu通过pci总线控制器访问不同的pci地址空间,就能访问到不同的pci设备。
pci IO空间是被淘汰的概念,与pci mem空间类似,每个pci 设备的pci io地址不一样。
比较特殊的是pci配置空间,但是没有pci配置空间,也就不可能配置IO空间和mem空间。
每个pci设备(这里只逻辑设备,一个pci物理设备可以有多个逻辑设备,就是所谓的多功能pci设备)的配置空间的地址,是一个32位的地址。由总线号+槽位号+功能号组成。
pci总线控制器决定了pci总线号。
每个pci总线控制器和pci设备相连时,有一根IDSEL信号线。IDSEL信号,一端接在pci设备的IDSEL端,另一头接在pci总线控制器的pci地址线AD[31:11]其中的一个(不会有2根IDSEL接在同一条AD[31:11]上)。这个独特的连接,决定了pci设备的slot号。
每个pci物理设备,可能含有多个逻辑设备,就是func号。
由总线号+槽位号+功能号组成的唯一的32位地址区分开pci的设备空间。
至此,pci设备的3种空间完全区分开了,每个pci设备也就区分开了。
至于linux driver,区分每个pci设备,只是靠struct pci_dev *pdev 这个结构体区分。
所有的linux内核以及驱动程序,都会小心地保存和传递这个结构体。
问:一个pci物理设备可以有多个逻辑设备,就是所谓的多功能pci设备,那一块pcie显卡 可以算是多个逻辑设备吗?显卡功能很多,以功能来区分成多个逻辑设备?
答:这个得看硬件设计,不能简单地看呈现出来的功能
例如一个pci卡扩展4个usb口 ,可能这4个usb口都来自一个逻辑设备,也可能来自4个逻辑设备
软件要区分,就看能够从这个物理设备读取到多少个配置空间。一个pci设备配置空间就是一个逻辑设备
你用lspci查看设备,
00:15.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.7 PCI bridge: VMware PCI Express Root Port (rev 01)
这些设备的总线号0和槽位号15都相同,是一个物理设备。但是含有8个逻辑设备。
问:哦,懂了,谢谢。再问,pci地址空间,这个空间有多大 ,是不是就是kmalloc所在的位置吧,但是这个和配置空间有什么区别?字符设备好像没有所谓的配置空间吧,配置空间具体干嘛用的,我们driver需要管吗?
答:pci设备的3种空间:配置空间、IO空间、mem空间 之间是互相独立的
这个配置空间对于驱动也仅仅起到配置pci mem空间、中断号之类的配置作用。
真正大数据量和大量使用的是pci mem空间,所以你的pci设备驱动注册的字符设备也只关心pci mem空间。
做BSP的很关心pci配置空间,做pci设备driver的,一般也就从pci配置空间读取个中断号和pci mem空间的基址和大小等有限的功能。也有可能大量操作pci配置空间,这个得看具体的pci设备硬件设计。
而且这3种空间跟cpu的内存空间也是互相独立的,不过pci mem空间即pci总线地址空间跟cpu内存空间存在映射关系。
你不可能用kmalloc申请到pci总线上的空间,kmalloc仅能操作cpu内存空间
看pci-skeleton.c这个例子,是靠ioremap映射pci总线地址到cpu内存地址。
推荐使用pci_ioremap ,这样更简单还安全,对于不可缓存的pci设备。
pci地址空间和cpu地址空间独立,但是又存在映射关系。所以,虽然pci地址空间可以有32/64位,但实际不可能达到这么大。cpu的内存地址空间,需要空出来一部分专门映射给pci总线。
例如32位cpu,分出来2GB的空间用作映射到pci地址空间上去的话,cpu仅剩2GB内存地址空间。这时候就不可能支持2GB以上的物理内存了。
同理,因为pci地址空间和cpu地址空间独立,所以显卡的显存,也别指望能分给cpu当内存。
cpu访问显卡的显存,也仅能通过映射的小窗口访问有限的显卡显存。
问:详细清楚,谢谢高手~
{做pci设备driver的,一般也就从pci配置空间读取个中断号和pci mem空间的基址和大小等有限的功能}
那我好像基本上就拿到个中断号而已,这个都是用内核注册函数去动态注册的,也就是通常内核函数在访问配置空间吧 ?
如果我的pci设备上有DDR内存在,这个内存就构成了PCI mem空间了吧,如果我的pci设备完全没有自带内存,flash什么的,也就谈不上PCI mem空间吧?那问题是,如果kmalloc是在cpu的内存地址上分配空间了,我要对pci设备自带的ddr内存操作,分配应该怎么操作呢?malloc这个函数应该也是在cpu内存上吧?
答:1、linux启动时,会初始化pci总线控制器,告知pci控制器,哪一部分的cpu内存地址能被映射到pci地址总线。这里仅仅是一个整体上的大划分。
然后扫描pci总线,探测设备,给设备分配中断号,以及在上面约定的范围内,给每一个pci设备分配具体的合法的pci总线基址,然后注册pdev
这个过程对于做bsp的需要熟知,仅写pci 设备driver的人不需要关心。
访问配置空间的内核函数,其实是pci总线控制器驱动提供的。
所有的物理总线,pci 、USB、I2c,其实都有总线控制器和总线上的设备的区别。这两端都需要写驱动。
谁要卖什么芯片,就必须提供什么驱动。pci控制器在南北桥里,卖南北桥的intel就把pci总线控制器驱动加入linux主线内核。
pci_resource_start pci_config_read 这些函数,严格说起来,是pci总线控制器驱动提供的。
一般公司的程序员,也就只能写写pci设备的驱动。
pci配置空间共256 byte,前64byte是标准的,后面允许自定义。这样pci 设备driver也可能会经常操作pci配置空间。这要看pci设备的具体设计。
2、不是只有ddr才当作寄存器。cpld 、fpga也能模拟出寄存器。pci设备没有内存,也可以有PCI mem空间
3、已经提到了,用ioremap或pci_ioremap 。需要配合pci设备的datasheet看。pci设备允许映射多大,如何通过pci配置空间来配置映射,都会写在手册里。
问:查看了一下 ,好像将自己pci上的内存ioremap()加入到了整个cpu的虚拟地址空间后,只能用ioread32/iowrite32对其片内ddr内存进行访问了?malloc kmalloc好像都无法使用是吗?
答:为什么纠结要使用malloc kmalloc 操作pci的地址空间呢?他们只能申请cpu内存。
现有的机制操作pci总线也足够了
pci_ioremap之后,例如pci_addr=pci_iomap(pdev, pci_bar, 0);
ioread32/iowrite32 可以, *(u32 *)(pci_addr) 也行,memcpy memset也行,用dma操作也行
不过ioread32会进行大小端的转换 ,pci总线上是小端
如果你的处理器是大端,必要时可以自己转一下字节序
问:简直是高人啊!谢谢谢谢
如果我写一个显卡驱动,我的显存是1g,我不把他映射给cpu,那我直接memcpy(0,char*,100);假设没有rom,0地址就是ddr内存,这样使用也可以的是吧,然后我再把处理玩的数据从显卡的显存中用DMA方式copy到cpu内存区,所以我就不用ioremap这种函数了,也就是我把ddr显存完全不给cpu/LINUX看到,这样设计可行吗?
答:linux不允许直接操作物理地址,即使在内核。
memcpy只能操作虚拟地址!你不能把物理地址传给memcpy
假设cpu物理地址 A ,对应pci总线地址B, 地址A和地址B分属不同的总线,一般A和B的数值相同。
虚拟地址C=ioremap(A),然后程序读写虚拟地址C ,等于读写cpu物理地址A,也等效于读写pci总线地址B。
反过来,pci设备也能主动读写pci地址B ,等效于读写了cpu物理地址A,等于读写了cpu虚拟地址C
这就是ioremap的作用,映射一片物理地址到虚拟地址,你不能不用ioremap就直接memcpy。
如果这个虚拟地址是ioremap到pci总线的,你才能操作pci设备。
先有ioremap,然后才能memcpy 。
至于显卡到底能映射对少显存给cpu,也不是驱动程序员能决定的。硬件设计就已经限制死了
当然 ,对于dma操作,你要告诉dma控制器的地址是上面说的A和B ,分别作源和目的
DMA是硬件控制器,不认虚拟地址
问:我网上查到了,说,对于pci设备,总线地址就是等于物理地址,why ?
我的理解,总线地址,比如pci设备自带ddr内存,从0开始,0x00他在pci的ddr内存的起始位置;物理地址,是cpu操作内存的地址,0x00在系统内存的起始位置,差好多的,为什么会相同
说相同的文章在这:http://www.docin.com/p-208080694.html
答:那篇文章有前提的,是在pc上,而且还说了,并非每个平台都如此。那本书是ldd,没有说错。
pci是intel主推的标准,是x86架构的脊梁---所有的外设都挂在pci/pcie总线上。x86的设计者为了方便程序员,一般都直接把cpu物理地址 和 pci总线地址不做任何偏移,直接一一映射。
这样,cpu物理地址和对应的pci总线地址,在数值上一样。所以有人误解为总线地址就是等于物理地址。在x86上这样写驱动不会有问题,x86数量庞大,也就强化了这种错误观念。
但是在powerpc上,cpu物理地址 和 pci总线地址映射时可以加偏移.
另外,pci设备自己看自己的资源,例如显卡硬件操作自己的显存,在linux通过配置空间给显卡分配一个pci总线地址前,显卡自己也不会响应任何pci地址总线上的任何读写请求,此时之接受pci配置空间的访问。
分配给显卡的pci总线地址,不会和cpu物理地址有任何重叠。
分配给显卡的pci总线地址,不会和cpu物理地址有任何重叠。
说错了
分配给显卡的pci总线地址,不会和其他设备在cpu可寻址的物理地址有任何重叠。就是显卡的pci总线地址,和cpu的ddr内存地址,即使属于不同总线,也不会重叠
原文见http://bbs.csdn.net/topics/380252112