主要说明

  1. Kernel版本:4.14
  2. ARM64处理器
一、PCI概述

从本文开始,将会针对PCIe专题来展开,涉及的内容包括:

  1. PCI/PCIe总线硬件;
  2. Linux PCI驱动核心框架;
  3. Linux PCI Host控制器驱动;

不排除会包含PCIe外设驱动模块,一切随缘。

作为专题的第一篇,当然会先从硬件总线入手。 进入主题前,先讲点背景知识。 在PC时代,随着处理器的发展,经历了几代I/O总线的发展,解决的问题都是CPU主频提升与外部设备访问速度的问题:

  1. 第一代总线包含ISA、EISA、VESA和Micro Channel等;
  2. 第二代总线包含PCI、AGP、PCI-X等;
  3. 第三代总线包含PCIe、mPCIe、m.2等;

PCIe(PCI Express)是目前PC和嵌入式系统中最常用的高速总线,PCIe在PCI的基础上发展而来,在软件上PCIe与PCI是后向兼容的,PCI的系统软件可以用在PCIe系统中。

二、PCI Local Bus2.1 PCI总线组成

先来看一下PCI Local Bus的系统架构图:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(1)

从图中看,与PCI总线相关的模块包括:

  1. Host Bridge,比如PC中常见的North Bridge(北桥); 图中处理器、Cache、内存子系统通过Host Bridge连接到PCI上,Host Bridge管理PCI总线域,是联系处理器和PCI设备的桥梁,完成处理器与PCI设备间的数据交换。其中数据交换,包含处理器访问PCI设备的地址空间和PCI设备使用DMA机制访问主存储器,在PCI设备用DMA访问存储器时,会存在Cache一致性问题,这个也是Host Bridge设计时需要考虑的; 此外,Host Bridge还可选的支持仲裁机制,热插拔等;
  2. PCI Local Bus; PCI总线,由Host Bridge或者PCI-to-PCI Bridge管理,用来连接各类设备,比如声卡、网卡、IDE接口等。可以通过PCI-to-PCI Bridge来扩展PCI总线,并构成多级总线的总线树,比如图中的PCI Local Bus #0和PCI Local Bus #1两条PCI总线就构成一颗总线树,同属一个总线域;
  3. PCI-To-PCI Bridge; PCI桥,用于扩展PCI总线,使采用PCI总线进行大规模系统互联成为可能,管理下游总线,并转发上下游总线之间的事务;
  4. PCI Device; PCI总线中有三类设备:PCI从设备,PCI主设备,桥设备。 PCI从设备:被动接收来自Host Bridge或者其他PCI设备的读写请求; PCI主设备:可以通过总线仲裁获得PCI总线的使用权,主动向其他PCI设备或主存储器发起读写请求; 桥设备:管理下游的PCI总线,并转发上下游总线之间的总线事务,包括PCI桥、PCI-to-ISA桥、PCI-to-Cardbus桥等。

2.2 PCI总线信号定义

PCI总线是一条共享总线,可以挂接多个PCI设备,PCI设备通过一系列信号与PCI总线相连,包括:地址/数据信号、接口控制信号、仲裁信号、中断信号等。如下图:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(2)

看一下C/BE[3:0]都有哪些命令吧:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(3)

更多linux内核视频教程文档资料免费领取后台私信【内核】自行获取.

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(4)

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(5)

Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂

2.3 PCI事务模型

PCI使用三种模型用于数据的传输:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(6)

  1. Programmed I/O:通过IO读写访问PCI设备空间;
  2. DMA:PIO的方式比较低效,DMA的方式可以直接去访问主存储器而无需CPU干预,效率更高;
  3. Peer-to-peer:两台PCI设备之间直接传送数据;
2.4 PCI总线地址空间映射

PCI体系架构支持三种地址空间:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(7)

  1. Memory空间: 针对32bit寻址,支持4G的地址空间,针对64bit寻址,支持16EB的地址空间;
  2. I/O空间 PCI最大支持4G的IO空间,但受限于x86处理器的IO空间(16bits带宽),很多平台将PCI的IO地址空间限定在64KB;
  3. 配置空间 x86 CPU可以直接访问memory空间和I/O空间,而配置空间则不能直接访问; 每个PCI功能最多可以有256字节的配置空间; PCI总线在进行配置的时候,采用ID译码方式,使用设备的ID号,包括Bus Number,Device Number,Function Number和Register Number,每个系统支持256条总线,每条总线支持32个设备,每个设备支持8个功能,由于每个功能最多有256字节的配置空间,因此总的配置空间大小为:256B * 8 * 32 * 256 = 16M; 有必要再进一步介绍一下配置空间: x86 CPU无法直接访问配置空间,通过IO映射的数据端口和地址端口间接访问PCI的配置空间,其中地址端口映射到0CF8h - 0CFBh,数据端口映射到0CFCh - 0CFFh;

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(8)

  1. CPU写CF8h端口,其中写的内容如图所示,BUS,Device,Function能标识出特定的设备功能,Doubleword来指定配置空间的具体某个寄存器;
  2. CPU可以IO读写CFCh端口,用于读取步骤1中的指定寄存器内容,或者写入指定寄存器内容。这个过程有点类似于通过I2C去配置外接芯片;

那具体的配置空间寄存器都是什么样的呢?每个功能256Byte,前边64Byte是Header,剩余的192Byte支持可选功能。有种类型的PCI功能:Bridge和Device,两者的Header都不一样。

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(9)

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(10)

配置空间中有个寄存器字段需要说明一下:Base Address Register,也就是BAR空间,当PCI设备的配置空间被初始化后,该设备在PCI总线上就会拥有一个独立的PCI总线地址空间,这个空间就是BAR空间,BAR空间可以存放IO地址空间,也可以存放存储器地址空间。

三、PCI Express3.1 PCIe体系结构

先看一下PCIe架构的组成图:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(11)

前文提到过,PCIe在软件上保持了后向兼容性,那么在PCIe的设计上,需要考虑在PCI总线上的软件视角,比如Root Complex的实现可能就如下图所示,从而看起来与PCI总线相差无异:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(12)

而Switch的实现可能如下图所示:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(13)

3.2 PCIe数据传输

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(14)

PCIe规范定义了分层的架构设计,包含三层:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(15)

  1. Transaction层

数据包的封装与解封装,与网络包的创建与解析很类似,如下图:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(16)

来一个更详细的PCIe分层图:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(17)

3.3 PCIe设备的配置空间

为了兼容PCI软件,PCIe保留了256Byte的配置空间,如下图:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(18)

此外,在这个基础上将配置空间扩展到了4KB,还进行了功能的扩展,比如Capability、Power Management、MSI中断等:

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(19)

linux驱动入门路线(一文搞懂LinuxPCI驱动框架)(20)

,