电子说
之前调试了几个PCI网卡驱动,虽然功能没什么问题,但驱动中调用的某些内核提供的PCI相关的接口一直没搞太清楚,所以最近准备深入研究一把。
PCI设备的识别及配置方式
PCI总线使用单端并行数据总线,采用地址译码方式进行数据传递,而采用ID译码方式进行配置信息(比如配置某个PCI设备的物理地址)的传递。其中地址译码方式使用地址信号,而ID译码方式使用PCI设备的ID号,包括Bus Number、Device Number、Function Number和Register Number。Linux系统中使用了Bus Number、Device Number和Function Number区分不同的设备;对于有两个接口的网卡,Linux会按照两个设备处理,因为这两个接口会呈现出不同的Function Number。
Posted和Non-Posted传送方式
PCI总线规定了两类数据传送方式,分别是Posted和Non-Posted数据传送方式。又分别对应了Posted和Non-Posted总线事务。
Posted总线事务指PCI主设备向PCI目标设备进行数据传递时,当数据到达PCI桥后,即由PCI桥接管来自上游总线的总线事务,并将其转发到下游总线。采用这种数据传递方式,在数据还没有到达最终的目的地之前,PCI总线就可以结束当前总线事务,从而在一定程度上解决了PCI总线的拥塞问题,所以这种方式的PCI总线利用率较高。只有存储器写请求可以采用Posted总线事务,简称为PMW(Posted Memory Write)。
Non-Posted总线事务是指PCI主设备向PCI目标设备进行数据传递时,数据必须到达最终目的地之后,才能结束当前总线事务的一种数据传递方式。存储器读请求、I/O读写请求、配置读写请求只能采用Non-Posted总线事务。
中断请求和数据传送的同步问题(MSI中断机制不存在这个问题)
在PCI总线中,INTx信号是一个异步信号。所谓异步,是指INTx信号的传递并不与PCI总线的数据传送同步。假设某个PCI设备在使用DMA方式将一组数据写入存储器,该设备在最后一个数据离开自己的发送FIFO时,即认为DMA操作已经完成。此时这个设备将通过INTx信号,通知处理器DMA写操作完成。
但是,当处理器收到INTx信号时,并不意味着PCI设备已经将数据写入存储器中,因为PCI设备的数据传递需要经过PCI桥/HOST主桥,最终才能到达存储器。
PCI总线提供了以下两种方法解决这个同步问题。
(1)PCI设备保证在数据到达目的地之后,再提交中断请求。具体来说,PCI设备在提交中断请求之前,向DMA写的数据区域发出一个读请求。PCI总线的“序”机制,保证了在这个存储器读请求完成前,会将以前发出的DMA数据写入存储器。PCI总线规范要求HOST主桥和PCI桥必须保证这种读操作可以刷新写操作。但问题是,没有多少芯片设计者愿意提供这种机制,因为这将极大地增加他们的设计难度。除此之外,使用这种方法也将增加中断请求的延时。
(2)中断服务程序在使用“PCI设备写入存储器”的这些数据之前,需要对这个PCI设备进行读操作。这个读操作也可以强制将数据最终写入存储器。这种方法也是利用了PCI总线的传送序规则,与第1种方法类似。只是这种方法使用软件方式,而第1种方式使用硬件方式。这是绝大多数处理器系统采用的方法。在中断服务程序中,往往都是先读取PCI设备的中断状态寄存器,判断中断产生原因之后,才对PCI设备写入的数据进行操作。这个读取中断状态寄存器的过程,一方面可以获得设备的中断状态,另一方面可以保证DMA写的数据(在被CPU读取前)已经到达存储器。
PCI配置空间中的几个寄存器的作用
(1)Cache Line Size寄存器:记录HOST处理器使用的Cache Line长度。在PCI总线中和Cache相关的总线事务,如存储器写并无效和Cache多行读等总线事务需要使用这个寄存器。该寄存器由系统软件设置,但是在PCI设备的运行过程中,只有其硬件逻辑才会使用该寄存器。对于PCIe设备,该寄存器的值无意义,因为PCIe设备在进行数据传送时,在其报文中含有一次数据传送的大小,PCIe总线控制器可以使用这个“大小”,判断数据区域与Cache Line的对应关系。
(2)Interrupt Line寄存器:由系统软件对PCI设备进行配置时写入,记录当前PCI设备使用的中断向量号。设备驱动程序可以通过这个寄存器,判断当前PCI设备使用处理器中的哪个中断向量号,并将驱动程序中的中断服务例程注册到操作系统中。但是,目前在绝大多数处理器系统中,并没有使用该寄存器存放PCI设备使用的中断向量号。
(3)Base Address Register 0~5 寄存器:简称为BAR寄存器。保存的是PCI设备在PCI总线域中的基地址,由操作系统配置。在PCI设备复位之后,该寄存器将存放PCI设备需要使用的基址空间大小、这段空间是I/O空间还是存储器空间、如果是存储器空间是否可预取等。
系统软件对PCI总线进行配置时,先获取BAR寄存器中的初始化信息,之后根据处理器系统的配置,将合理的基地址写入相应的BAR寄存器中。系统软件还可以使用该寄存器获得PCI设备使用的BAR空间的长度,方法是向BAR寄存器中写入0xFFFFFFFF,然后再读取该寄存器。
(4)Command寄存器:在初始化时,其值为0,此时这个PCI设备除了能够接收配置请求总线事务外,不能接收任何存储器或者I/O请求。在Linux系统中,设备驱动程序调用pci_enable_device函数(也可以是pci_enable_device_mem-> pci_enable_device_flags-> do_pci_enable_device-> pcibios_enable_device-> pci_enable_resources-> pci_write_config_word),使能该寄存器的I/O和Memory Space位之后,才能访问该设备的存储器或者I/O地址空间。
另外,该寄存器中还有Bus Master位表示该设备是否可以作为主设备(Linux驱动程序中调用pci_set_master函数,将此位设置为1,表示设备可作为master)。寄存器中的Interrupt Disable位默认为0,为1时表示不允许该设备使用INTx信号提交中断请求,当PCI设备使用MSI中断方式时,该位将被设置为1(Linux驱动程序中调用的函数为pci_alloc_irq_vectors-> pci_alloc_irq_vectors_affinity-> __pci_enable_msi_range-> msi_capability_init-> pci_intx_for_msi-> pci_intx)。
Linux的PCI设备驱动程序中调用的dma_set_mask_and_coherent函数有什么作用
Linux的PCI设备驱动中一般都会调用诸如dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))之类的接口。这是用来通知内核当前设备支持的DMA访问的寻址能力,作用是限制内核为当前设备分配的内存地址范围。比如之前的例子就表示当前设备支持64位寻址,并且在此范围内支持一致性DMA访问。
Linux的PCI设备驱动程序中调用的pci_disable_link_state函数有什么作用
有些PCI设备的驱动中会调用诸如pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM)之类的接口。其作用是设置PCIe设备的扩展配置空间中的Link Control寄存器。具体到前例,是把Link Control寄存器中的“ASPM Control”字段和“Enable clkreq”字段设置为0,作用分别是禁止PCI设备进入L0s和L1(低功耗)状态以及禁止CLKREQ功能(相当于禁止设备的时钟管理功能,起到性能优先的效果,不考虑省电;具体效果还取决于内核中CONFIG_PCIEASPM_XXXX相关的配置)。
全部0条评论
快来发表一下你的评论吧 !