【原创】Linux PCI驱动框架分析(三)

背 景

  • Read the fucking source code! –By 鲁迅
  • A picture is worth a thousand words. –By 高尔基

说明:

  1. Kernel版本:4.14
  2. ARM64处理器
  3. 使用工具:Source Insight 3.5, Visio

  4. 概述

先回顾一下PCIe的架构图:

【原创】Linux PCI驱动框架分析(三)
  • 本文将讲PCIe Host的驱动,对应为 Root Complex部分,相当于PCI的 Host Bridge部分;
  • 本文会选择Xilinx的 nwl-pcie来进行分析;
  • 驱动的编写整体偏简单,往现有的框架上套就可以了,因此不会花太多笔墨,点到为止;

  • 流程分析

  • 但凡涉及到驱动的分析,都离不开驱动模型的介绍,驱动模型的实现让具体的驱动开发变得更容易;

  • 所以,还是回顾一下上篇文章提到的驱动模型:Linux内核建立了一个统一的设备模型,分别采用总线、设备、驱动三者进行抽象,其中设备与驱动都挂在总线上,当有新的设备注册或者新的驱动注册时,总线会去进行匹配操作( match函数),当发现驱动与设备能进行匹配时,就会执行probe函数的操作;

【原创】Linux PCI驱动框架分析(三)
  • 《Linux PCI驱动框架分析(二)》中提到过PCI设备、PCI总线和PCI驱动的创建,PCI设备和PCI驱动挂接在PCI总线上,这个理解很直观。针对PCIe的控制器来说,同样遵循设备、总线、驱动的匹配模型,不过这里的总线是由虚拟总线 platform总线来替代,相应的设备和驱动分别为 platform_deviceplatform_driver

那么问题来了, platform_device是在什么时候创建的呢?那就不得不提到 Device Tree设备树了。

2.1 Device Tree

  • 设备树用于描述硬件的信息,包含节点各类属性,在dts文件中定义,最终会被编译成dtb文件加载到内存中;
  • 内核会在启动过程中去解析dtb文件,解析成 device_node描述的 Device Tree
  • 根据 device_node节点,创建 platform_device结构,并最终注册进系统,这个也就是PCIe Host设备的创建过程;

我们看看PCIe Host的设备树内容:

pcie: pcie@fd0e0000 {
    compatible = "xlnx,nwl-pcie-2.11";
    status = "disabled";
    #address-cells = ;
    #size-cells = ;
    #interrupt-cells = ;
    msi-controller;
    device_type = "pci";

    interrupt-parent = ;
    interrupts = ,
             ,
             ,
             ,  /* MSI_1 [63...32] */
             ;  /* MSI_0 [31...0] */
    interrupt-names = "misc", "dummy", "intx", "msi1", "msi0";
    msi-parent = ;

    reg = ,
          ,
          ;
    reg-names = "breg", "pcireg", "cfg";
    ranges = ;/* prefetchable memory */
    bus-range = ;

    interrupt-map-mask = ;
    interrupt-map =     ,
                ,
                ,
                ;

    pcie_intc: legacy-interrupt-controller {
        interrupt-controller;
        #address-cells = ;
        #interrupt-cells = ;
    };
};

关键字段描述如下:

  • compatible:用于匹配PCIe Host驱动;
  • msi-controller:表示是一个MSI( Message Signaled Interrupt)控制器节点,这里需要注意的是,有的SoC中断控制器使用的是GICv2版本,而GICv2并不支持MSI,所以会导致该功能的缺失;
  • device-type:必须是 "pci"
  • interrupts:包含NWL PCIe控制器的中断号;
  • interrupts-namemsi1, msi0用于MSI中断, intx用于旧式中断,与 interrupts中的中断号对应;
  • reg:包含用于访问PCIe控制器操作的寄存器物理地址和大小;
  • reg-name:分别表示 Bridge registersPCIe Controller registersConfiguration space region,与 reg中的值对应;
  • ranges:PCIe地址空间转换到CPU的地址空间中的范围;
  • bus-range:PCIe总线的起始范围;
  • interrupt-map-maskinterrupt-map:标准PCI属性,用于定义PCI接口到中断号的映射;
  • legacy-interrupt-controller:旧式的中断控制器;

2.2 probe流程

  • 系统会根据dtb文件创建对应的platform_device并进行注册;
  • 当驱动与设备通过 compatible字段匹配上后,会调用probe函数,也就是 nwl_pcie_probe

【原创】Linux PCI驱动框架分析(三)

看一下 nwl_pcie_probe函数:

【原创】Linux PCI驱动框架分析(三)
  • 通常probe函数都是进行一些初始化操作和注册操作:
  • 初始化包括:数据结构的初始化以及设备的初始化等,设备的初始化则需要获取硬件的信息(比如寄存器基地址,长度,中断号等),这些信息都从DTS而来;
  • 注册操作主要是包含中断处理函数的注册,以及通常的设备文件注册等;

  • 针对PCI控制器的驱动,核心的流程是需要分配并初始化一个 pci_host_bridge结构,最终通过这个 bridge去枚举PCI总线上的所有设备;

  • devm_pci_alloc_host_bridge:分配并初始化一个基础的 pci_hsot_bridge结构;
  • nwl_pcie_parse_dt:获取DTS中的寄存器信息及中断信息,并通过 irq_set_chained_handler_and_data设置 intx中断号对应的中断处理函数,该处理函数用于中断的级联;
  • nwl_pcie_bridge_init:硬件的Controller一堆设置,这部分需要去查阅Spec,了解硬件工作的细节。此外,通过 devm_request_irq注册 misc中断号对应的中断处理函数,该处理函数用于控制器自身状态的处理;
  • pci_parse_request_of_pci_ranges:用于解析PCI总线的总线范围和总线上的地址范围,也就是CPU能看到的地址区域;
  • nwl_pcie_init_irq_domainmwl_pcie_enable_msi与中断级联相关,下个小节介绍;
  • pci_scan_root_bus_bridge:对总线上的设备进行扫描枚举,这个流程在 Linux PCI驱动框架分析(二)中分析过。 brdige结构体中的 pci_ops字段,用于指向PCI的读写操作函数集,当具体扫描到设备要读写配置空间时,调用的就是这个函数,由具体的Controller驱动实现;

2.3 中断处理

PCIe控制器,通过PCIe总线连接各种设备,因此它本身充当一个中断控制器,级联到上一层的中断控制器(比如GIC),如下图:

【原创】Linux PCI驱动框架分析(三)
  • PCIe总线支持两种中断的处理方式:
  • Legacy Interrupt:总线提供 INTA#, INTB#, INTC#, INTD#四根中断信号,PCI设备借助这四根信号使用电平触发方式提交中断请求;
  • MSI(Message Signaled Interrupt) Interrupt:基于消息机制的中断,也就是往一个指定地址写入特定消息,从而触发一个中断;

针对两种处理方式, NWL PCIe驱动中,实现了两个 irq_chip,也就是两种方式的中断控制器:

【原创】Linux PCI驱动框架分析(三)
  • irq_domain对应一个中断控制器( irq_chip), irq_domain负责将硬件中断号映射到虚拟中断号上;
  • 来一张旧图吧,具体文章可以去参考中断子系统相关文章;

【原创】Linux PCI驱动框架分析(三)

再来看一下 nwl_pcie_enable_msi函数:

【原创】Linux PCI驱动框架分析(三)
  • 在该函数中主要完成的工作就是设置级联的中断处理函数,级联的中断处理函数中最终会去调用具体的设备的中断处理函数;

所以,稍微汇总一下,作为两种不同的中断处理方式,套路都是一样的,都是创建 irq_chip中断控制器,为该中断控制器添加 irq_domain,具体设备的中断响应流程如下:

  1. 设备连接在PCI总线上,触发中断时,通过PCIe控制器充当的中断控制器路由到上一级控制器,最终路由到CPU;
  2. CPU在处理PCIe控制器的中断时,调用它的中断处理函数,也就是上文中提到过的 nwl_pcie_leg_handlernwl_pcie_msi_handler_high,和 nwl_pcie_leg_handler_low
  3. 在级联的中断处理函数中,调用 chained_irq_enter进入中断级联处理;
  4. 调用 irq_find_mapping找到具体的PCIe设备的中断号;
  5. 调用 generic_handle_irq触发具体的PCIe设备的中断处理函数执行;
  6. 调用 chained_irq_exit退出中断级联的处理;

2.4 总结

  • PCIe控制器驱动,各家的IP实现不一样,驱动的差异可能会很大,单独分析一个驱动毕竟只是个例,应该去掌握背后的通用框架;
  • 各类驱动,大体都是硬件初始化配置,资源申请注册,核心是处理与硬件的交互(一般就是中断的处理),如果需要用户来交互的,则还需要注册设备文件,实现一堆 file_operation操作函数集;
  • 好吧,我个人不太喜欢分析某个驱动,草草收场了;

下篇开始,继续回归到虚拟化,期待一下吧。

参考

Documentation/devicetree/bindings/pci/xlinx-nwl-pcie.txt

欢迎关注个人公众号,不定期分享技术文章:

【原创】Linux PCI驱动框架分析(三)

Original: https://www.cnblogs.com/LoyenWang/p/14255906.html
Author: LoyenWang
Title: 【原创】Linux PCI驱动框架分析(三)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/585941/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Ubuntu16.04修改IP

    ssh登录到服务。编辑网卡配置文件。 vim /etc/network/interfaces 先关闭DPCP,将 iface eth0 inet dhcp前面加上#号。 设置IP地…

    Linux 2023年6月6日
    094
  • Shell语法

    在 Shell 中引号分为 2 种:单引号、双引号。 ( 1 )双引号 由双引号括起来的字符,除 $ 、倒引号和反斜线( \ )仍保留其特殊功能外,其余字符通常作为普通字符对待。 …

    Linux 2023年5月28日
    077
  • Linux进度条制作

    进度条 先了解一下/r 的用法 /r 讲光标回到当前行的最开始 4 int main() 5 { 6 int i=0; 7 for(i=0;i10;i++) 8 { 9 print…

    Linux 2023年6月13日
    080
  • 4.1 打包和压缩的概念和区别

    在讲解具体的归档命令和压缩命令之前,先来了解一下归档和压缩所各自代表的含义。 归档,也称为打包,指的是一个文件或目录的集合,而这个集合被存储在一个文件中。归档文件没有经过压缩,因此…

    Linux 2023年6月7日
    081
  • ansible批量采集、批量互信、批量复制、分发文件

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年6月7日
    089
  • Workshop与会议形式的分类

    在计算机科研工作阅读的过程中,我们经常遇到workshop和conference等关键词,通过这些关键词我们可以一定程度上了解文章的水平,从而方便自己的学习,下面记录了一些我从网络…

    Linux 2023年6月14日
    0170
  • 019 Linux tcpdump 抓包案例入门可真简单啊?

    1 tcpdump 是什么? 2 tcpdump 常用命令参数 3 tcpdump 抓包wss,配合Wireshark分析 4 tcpdump 抓包白度,配合Wireshark分析…

    Linux 2023年5月27日
    078
  • ES6

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> &lt…

    Linux 2023年6月13日
    083
  • gerrit系统如何配置访问控制

    .版本:v0.3作者:河东西望日期:2022-7-13. gerrit系统的上手使用有两个难点: 想要上手使用gerrit的同仁们,搭建部署好gerrit系统之后,会发现gerri…

    Linux 2023年6月7日
    086
  • 编写一个简单的linux kernel rootkit

    一、前言 linux kernel rootkit跟普通的应用层rootkit个人感觉不大,个人感觉区别在于一个运行在用户空间中,一个运行在内核空间中;另一个则是编写时调用的API…

    Linux 2023年6月8日
    0101
  • 博客园装饰——(二)滚动到页面顶部或底部

    功能描述: 1. 当页面向下滚动一定距离时,向下滚动到底部的按钮以淡入的效果出现,并以固定定位显示。且滚动到一定距离(快接近所设置的底部)时,该按钮又会以淡出效果消失。 2. 当页…

    Linux 2023年6月14日
    080
  • 008 Linux 文件查找 find

    在 Linux 系统,find 毫无疑问是最强的文件查找工具。find 一般会与其他命令结合,将查找到的结果作为参数传入到后置命令中,进行删除、统计、复制迁移等操作。 find /…

    Linux 2023年5月27日
    088
  • 【XML】学习笔记第四章-schema

    Schema 概述 作用 与DTD相比Schema的优势 基础命名空间: 模式 引用方法 通过xsi:noNamespaceSchemaLocation引入 通过xsi:shema…

    Linux 2023年6月14日
    080
  • jmeter&WebSocket直播间性能测试

    http://t.csdn.cn/GfXzJ 1.Jmeter本身是支持HTTP方法,要测试WebSocket的接口,首先要安装插件。 安装插件的方法:1)首先下载插件管理器① 访…

    Linux 2023年6月8日
    082
  • 我为儿子开发的第一款Android App,用于九九乘法练习

    用一天时间在macbook上安装好了Android Studio For Mac,注意dl.google.com只支持电信网络下载,家里宽带如果是移动或者联通的,使用AS下载And…

    Linux 2023年6月14日
    085
  • Linux下的SELINUX

    理解Linux下的SELinux 长久以来,每当遇到授权问题或者新安装的主机,我的第一反应是通过 setenforce 0命令禁用SELinux,来减少产生的权限问题,但是这并不是…

    Linux 2023年6月7日
    081
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球