【原创】Linux PCI驱动框架分析(三)
背 景
Read the fucking source code!
--By 鲁迅A picture is worth a thousand words.
--By 高尔基
说明:
- Kernel版本:4.14
- ARM64处理器
- 使用工具:Source Insight 3.5, Visio
1. 概述
先回顾一下PCIe的架构图:
- 本文将讲PCIe Host的驱动,对应为
Root Complex
部分,相当于PCI的Host Bridge
部分; - 本文会选择Xilinx的
nwl-pcie
来进行分析; - 驱动的编写整体偏简单,往现有的框架上套就可以了,因此不会花太多笔墨,点到为止;
2. 流程分析
- 但凡涉及到驱动的分析,都离不开驱动模型的介绍,驱动模型的实现让具体的驱动开发变得更容易;
- 所以,还是回顾一下上篇文章提到的驱动模型:Linux内核建立了一个统一的设备模型,分别采用总线、设备、驱动三者进行抽象,其中设备与驱动都挂在总线上,当有新的设备注册或者新的驱动注册时,总线会去进行匹配操作(
match
函数),当发现驱动与设备能进行匹配时,就会执行probe函数的操作;
《Linux PCI驱动框架分析(二)》
中提到过PCI设备、PCI总线和PCI驱动的创建,PCI设备和PCI驱动挂接在PCI总线上,这个理解很直观。针对PCIe的控制器来说,同样遵循设备、总线、驱动的匹配模型,不过这里的总线是由虚拟总线platform
总线来替代,相应的设备和驱动分别为platform_device
和platform_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 = <3>;
#size-cells = <2>;
#interrupt-cells = <1>;
msi-controller;
device_type = "pci";
interrupt-parent = <&gic>;
interrupts = <0 118 4>,
<0 117 4>,
<0 116 4>,
<0 115 4>, /* MSI_1 [63...32] */
<0 114 4>; /* MSI_0 [31...0] */
interrupt-names = "misc", "dummy", "intx", "msi1", "msi0";
msi-parent = <&pcie>;
reg = <0x0 0xfd0e0000 0x0 0x1000>,
<0x0 0xfd480000 0x0 0x1000>,
<0x80 0x00000000 0x0 0x1000000>;
reg-names = "breg", "pcireg", "cfg";
ranges = <0x02000000 0x00000000 0xe0000000 0x00000000 0xe0000000 0x00000000 0x10000000 /* non-prefetchable memory */
0x43000000 0x00000006 0x00000000 0x00000006 0x00000000 0x00000002 0x00000000>;/* prefetchable memory */
bus-range = <0x00 0xff>;
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc 0x1>,
<0x0 0x0 0x0 0x2 &pcie_intc 0x2>,
<0x0 0x0 0x0 0x3 &pcie_intc 0x3>,
<0x0 0x0 0x0 0x4 &pcie_intc 0x4>;
pcie_intc: legacy-interrupt-controller {
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
};
};
关键字段描述如下:
compatible
:用于匹配PCIe Host驱动;msi-controller
:表示是一个MSI(Message Signaled Interrupt
)控制器节点,这里需要注意的是,有的SoC中断控制器使用的是GICv2版本,而GICv2并不支持MSI,所以会导致该功能的缺失;device-type
:必须是"pci"
;interrupts
:包含NWL PCIe控制器的中断号;interrupts-name
:msi1, msi0
用于MSI中断,intx
用于旧式中断,与interrupts
中的中断号对应;reg
:包含用于访问PCIe控制器操作的寄存器物理地址和大小;reg-name
:分别表示Bridge registers
,PCIe Controller registers
,Configuration space region
,与reg
中的值对应;ranges
:PCIe地址空间转换到CPU的地址空间中的范围;bus-range
:PCIe总线的起始范围;interrupt-map-mask
和interrupt-map
:标准PCI属性,用于定义PCI接口到中断号的映射;legacy-interrupt-controller
:旧式的中断控制器;
2.2 probe流程
- 系统会根据dtb文件创建对应的platform_device并进行注册;
- 当驱动与设备通过
compatible
字段匹配上后,会调用probe函数,也就是nwl_pcie_probe
;
看一下nwl_pcie_probe
函数:
- 通常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_domain
和mwl_pcie_enable_msi
与中断级联相关,下个小节介绍;pci_scan_root_bus_bridge
:对总线上的设备进行扫描枚举,这个流程在Linux PCI驱动框架分析(二)
中分析过。brdige
结构体中的pci_ops
字段,用于指向PCI的读写操作函数集,当具体扫描到设备要读写配置空间时,调用的就是这个函数,由具体的Controller驱动实现;
2.3 中断处理
PCIe控制器,通过PCIe总线连接各种设备,因此它本身充当一个中断控制器,级联到上一层的中断控制器(比如GIC),如下图:
- PCIe总线支持两种中断的处理方式:
- Legacy Interrupt:总线提供
INTA#, INTB#, INTC#, INTD#
四根中断信号,PCI设备借助这四根信号使用电平触发方式提交中断请求; - MSI(
Message Signaled Interrupt
) Interrupt:基于消息机制的中断,也就是往一个指定地址写入特定消息,从而触发一个中断;
- Legacy Interrupt:总线提供
针对两种处理方式,NWL PCIe
驱动中,实现了两个irq_chip
,也就是两种方式的中断控制器:
irq_domain
对应一个中断控制器(irq_chip
),irq_domain
负责将硬件中断号映射到虚拟中断号上;- 来一张旧图吧,具体文章可以去参考中断子系统相关文章;
再来看一下nwl_pcie_enable_msi
函数:
- 在该函数中主要完成的工作就是设置级联的中断处理函数,级联的中断处理函数中最终会去调用具体的设备的中断处理函数;
所以,稍微汇总一下,作为两种不同的中断处理方式,套路都是一样的,都是创建irq_chip
中断控制器,为该中断控制器添加irq_domain
,具体设备的中断响应流程如下:
- 设备连接在PCI总线上,触发中断时,通过PCIe控制器充当的中断控制器路由到上一级控制器,最终路由到CPU;
- CPU在处理PCIe控制器的中断时,调用它的中断处理函数,也就是上文中提到过的
nwl_pcie_leg_handler
,nwl_pcie_msi_handler_high
,和nwl_pcie_leg_handler_low
; - 在级联的中断处理函数中,调用
chained_irq_enter
进入中断级联处理; - 调用
irq_find_mapping
找到具体的PCIe设备的中断号; - 调用
generic_handle_irq
触发具体的PCIe设备的中断处理函数执行; - 调用
chained_irq_exit
退出中断级联的处理;
2.4 总结
- PCIe控制器驱动,各家的IP实现不一样,驱动的差异可能会很大,单独分析一个驱动毕竟只是个例,应该去掌握背后的通用框架;
- 各类驱动,大体都是硬件初始化配置,资源申请注册,核心是处理与硬件的交互(一般就是中断的处理),如果需要用户来交互的,则还需要注册设备文件,实现一堆
file_operation
操作函数集; - 好吧,我个人不太喜欢分析某个驱动,草草收场了;
下篇开始,继续回归到虚拟化,期待一下吧。
参考
Documentation/devicetree/bindings/pci/xlinx-nwl-pcie.txt
欢迎关注个人公众号,不定期分享技术文章:
【原创】Linux PCI驱动框架分析(三)的更多相关文章
- 【原创】Linux PCI驱动框架分析(二)
背 景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本 ...
- 【原创】Linux PCI驱动框架分析(一)
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- Linux USB驱动框架分析 【转】
转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结 ...
- Linux USB驱动框架分析【转】
转自:http://blog.csdn.net/jeffade/article/details/7701431 Linux USB驱动框架分析(一) 初次接触和OS相关的设备驱动编写,感觉还挺有意思的 ...
- linux驱动基础系列--linux spi驱动框架分析
前言 主要是想对Linux 下spi驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.设备模型等也不进行详细说明原理.如果有任何错误地方,请指出,谢谢! spi ...
- linux驱动基础系列--linux spi驱动框架分析(续)
前言 这篇文章是对linux驱动基础系列--linux spi驱动框架分析的补充,主要是添加了最新的linux内核里设备树相关内容. spi设备树相关信息 如之前的文章里所述,控制器的device和s ...
- Linux USB驱动框架分析(2)【转】
转自:http://blog.chinaunix.net/uid-23046336-id-3243543.html 看了http://blog.chinaunix.net/uid-11848011 ...
- linux设备驱动程序--串行通信驱动框架分析
linux 串行通信接口驱动框架 在学习linux内核驱动时,不论是看linux相关的书籍,又或者是直接看linux的源码,总是能在linux中看到各种各样的框架,linux内核极其庞杂,linux各 ...
- uart驱动框架分析(二)uart_add_one_port
作者:lizuobin (百问网论坛答疑助手) 原文: https://blog.csdn.net/lizuobin2/article/details/51801183 (所用开发板:mini2440 ...
随机推荐
- 建立windows认证模式下的用户登录
第一步:点击控制面板-----管理工具------计算机管理 ,在操作系统的计算机管理界面下,展开本地用户和组,在用户下建立三个用户u1,u2,u3,密码与用户名相同,如图所示. 然后新建一个组叫QQ ...
- IKUN python 反序列化
题目过程1.一开始提示说要买到V6,观察源码,发现/static/img/lv/lv4.png.注册之后尝试寻找V6.观察url发现/shop?page=2.尝试写脚本匹配一下.发现在第181页. i ...
- C#软件性能优化
C#软件性能优化 1. 性能 衡量一个软件系统性能的常见指标有:响应时间.负载.资源使用率.并发数.在软件中有具体的提高性能需求时,我们需分析该系统性能的影响由哪些因素组成,再针对各部分进行性能 ...
- 新挖个坑,准备学习一下databricks的spark博客
挖坑 https://databricks.com/blog 一.spark3.0特性(Introducing Apache Spark 3.0) 1.通过通过自适应查询执行,动态分区修剪和其他优化使 ...
- AOP 有几种实现方式?
1. 回顾 AOP 是什么? 维基百科解释如下: 面向切面的程序设计(Aspect-oriented programming,AOP,又译作面向方面的程序设计.剖面导向程序设计)是计算机科学中的一种程 ...
- JavaSE12-内部类&API
1. 参数传递 1.1 类名作为形参和返回值 1.类名作为方法的形参 方法的形参是类名,其实需要的是该类的对象 实际传递的是该对象的[地址值] 2.类名作为方法的返回值 方法的返回值是类名,其实返回的 ...
- 记一次 HBase Master is initializing 问题处理
问题 hbase shell中建立建表出错 分析 org.apache.hadoop.hbase.PleaseHoldException: Master is initializing代表Master ...
- 2020-2021-1 20209307《Linux内核原理与分析》第一周作业
一.Linux基础命令操作 1.查看目录.新建文件.复制移除文件等 ls[选项] [文件或目录] -a 显示所有文件 包含隐藏文件 -l显示详细信息 -d查看目录属性 pwd显示当前目录 mkdir ...
- 工具-Redis-与Python一起使用(99.6.3)
@ 目录 1.安装 2.使用 以下为对应的方法 3.使用string为例子 关于作者 1.安装 pip install redis 2.使用 pip install redis from redis ...
- 5分钟教你在Linux下安装VMware
如果我们只有一台笔记本,又想要搭建一个小集群,怎么办?虚拟机帮你实现梦想,市面上较为常用的虚拟机软件有VMware.VirtualBox.Xen.KVM.hyper-v等,本文主要介绍如何在Linux ...