暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

pcie(五)- Linux PCI 驱动程序

囧囧妹 2022-08-11
261

点击上方蓝字【囧囧妹】一起学习,一起成长!

一,启用pcie设备

在接触任何设备寄存器之前,驱动程序需要通过调用pci_enable_device()来启用PCI设备,该函数内部会做如下操作:

  • 如果设备处于挂起状态,则唤醒设备,

  • 分配设备的 I/O 和内存区域(如果 BIOS 没有),

  • 分配一个 IRQ(如果 BIOS 没有)。

注意:

1,pci_enable_device()会失败!需要检查返回值。

2,OS BUG:在启用这些资源之前,系统不会检查资源分配,如果在调用 pci_request_resources() 之前调用pci_enable_device(),当两个设备分配到相同的范围时,设备驱动程序无法检测到该错误,这不是一个常见问题,但目前linux还不能很快解决该问题。


pci_set_master()将通过设置 PCI_COMMAND 寄存器中的总线主控位来启用 DMA。如果 BIOS 将其设置为虚假的,它还会修复延迟计时器值。pci_clear_master()将通过清除总线主机位来禁用 DMA。

如果 PCI 设备可以使用 PCI Memory-Write-Invalidate 事务,调用pci_set_mwi()这将为 Mem-Wr-Inval 启用 PCI_COMMAND 位,并确保正确设置高速缓存行大小寄存器,检查返回值,因为pci_set_mwi()并非所有架构或芯片组都可能支持 Memory-Write-Invalidate,虽然Mem-Wr-Inval很好用但如果不是必需的,最好调用pci_try_set_mwi()以让系统尽最大努力启用 Mem-Wr-Inval。

二,请求MMIO/IOP资源
内存 (MMIO) 和 I/O 端口地址不应直接从 PCI 设备配置空间中读取。使用 pci_dev 结构中的值,因为 PCI “总线地址”可能已被架构/芯片组特定内核支持重新映射到“主机物理”地址。
设备驱动程序需要调用pci_request_region()以验证没有其他设备已经在使用相同的地址资源。相反,驱动程序应该在调用pci_release_region()之后调用pci_disable_devie(),为的是防止两个设备在同一地址范围内发生冲突。

注意,驱动程序只能在_after_调用后才能确定MMIO和IO Port资源可用性pci_enable_device()

《io_mapping函数相关扩展》
linux/io-mapping.h 中的 io_mapping 函数提供了一种抽象,可以有效地将 I/O 设备的小区域映射到 CPU。最初的用途是支持 32 位处理器上的大图形孔径,其中 ioremap_wc 不能用于将整个孔径静态映射到 CPU,因为它会消耗过多的内核地址空间。
在驱动程序初始化期间使用以下方法创建映射对象:
    struct io_mapping *io_mapping_create_wc(unsigned long base, unsigned long size)
    “base”是要映射的区域的总线地址
    “size”表示要启用的映射区域有多大。两者都以字节为单位。

    带_wc后缀的函数提供了一种映射,它只能与 io_mapping_map_atomic_wc()、io_mapping_map_local_wc() 或 io_mapping_map_wc()一起使用。
    使用此映射对象,可以根据要求临时或长期映射各个页面。当然,临时map效率更高。它们有两种方式:
      void *io_mapping_map_local_wc(struct io_mapping *mapping,
      unsigned long offset)


      void *io_mapping_map_atomic_wc(struct io_mapping *mapping,
                                     unsigned long offset)
      'offset' 定义的映射区域内的偏移量。访问创建函数中指定区域之外的地址会产生未定义的结果。使用不是页面对齐的偏移量会产生未定义的结果。返回值指向 CPU 地址空间中的单个页面。
      带_wc后缀的函数向页面返回一个写入组合映射,并且只能与io_mapping_create_wc() 创建的映射一起使用
      临时映射仅在调用者的上下文中有效。不保证映射是全局可见的。
      io_mapping_map_local_wc()对X86 32结构位有影响,因为它禁用迁移。
      io_mapping_map_atomic_wc()禁用抢占和页面错误,最好不要在代码中使用,可以使用io_mapping_map_local_wc()替换。
      嵌套映射需要以相反的顺序撤消,因为映射使用堆栈来跟踪它们:
        addr1 = io_mapping_map_local_wc(map1, offset1);
        addr2 = io_mapping_map_local_wc(map2, offset2);
        ...
        io_mapping_unmap_local(addr2);
        io_mapping_unmap_local(addr1);
        映射随以下内容发布:
          void io_mapping_unmap_local(void *vaddr)
          void io_mapping_unmap_atomic(void *vaddr)
          'vaddr' 必须是最后一次 io_mapping_map_local_wc() 或 io_mapping_map_atomic_wc() 调用返回的值。这将取消映射指定的映射并撤消映射函数的影响。
          如果您需要在持有映射时sleep,则可以使用常规函数,但这样会导致程序运行很慢:
            void *
            io_mapping_map_wc(struct
            io_mapping *mapping, unsigned long offset)
            映射随以下内容发布:
              void io_mapping_unmap(void *vaddr)
              用于使用 io_mapping_map_wc() 映射的页面。
              在驱动程序关闭时,必须释放 io_mapping 对象:
                void io_mapping_free(struct io_mapping *mapping)
                觉得不错,点击“分享”,“赞”,“在看”传播给更多热爱嵌入式的小伙伴吧!
                文章转载自囧囧妹,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                评论