1.2.8判断pcie设备是否支持雷电技术

Intel具有一种基于Thunderbolt技术的PCIE变体,它结合了DisplayPort和PCIe协议,与Mini DisplayPort兼容。

Thunderbolt技术融合两种通信方法或者说协议,其中PCI Express用于数据传输,可以连接几乎任何类型的设备,DisplayPort用于显示,能同步传输1080p乃至超高清视频和最多八声道音频。

因此代码只在intel生产的设备中进行判别。

set_pcie_thunderbolt()

while ((vsec = pci_find_next_ext_capability(dev, vsec,
PCI_EXT_CAP_ID_VNDR))) {
pci_read_config_dword(dev, vsec + PCI_VNDR_HEADER, &header); /* Is the device part of a Thunderbolt controller? */
//设备是否具有雷电控制器
if (dev->vendor == PCI_VENDOR_ID_INTEL &&
PCI_VNDR_HEADER_ID(header) == PCI_VSEC_ID_INTEL_TBT) {
dev->is_thunderbolt = 1;
return;
}
}

其中有

#define PCI_VNDR_HEADER_ID(x)	((x) & 0xffff)
#define PCI_VSEC_ID_INTEL_TBT 0x1234 //雷电接口

1.2.9修复某些特殊的bug

对于某些bug,只存在于特定体系或设备,无法在此处进行列举,因此提供一个hook用于修复特殊设备的bug。而hook通过内核配置情况进行挂载。

pci_fixup_device()

//查找是否存在厂商号设备号相同的情况
for (; f < end; f++)
if ((f->class == (u32) (dev->class >> f->class_shift) ||
f->class == (u32) PCI_ANY_ID) &&
(f->vendor == dev->vendor ||
f->vendor == (u16) PCI_ANY_ID) &&
(f->device == dev->device ||
f->device == (u16) PCI_ANY_ID)) {
void (*hook)(struct pci_dev *dev); //获取hook函数指针,用于修复特定bug
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
hook = offset_to_ptr(&f->hook_offset);
#else
hook = f->hook;
#endif
calltime = fixup_debug_start(dev, hook);
hook(dev);
fixup_debug_report(dev, calltime, hook);
}

其中hook可有由

#define DECLARE_PCI_FIXUP_CLASS_EARLY(vendor, device, class,	\
class_shift, hook) \
DECLARE_PCI_FIXUP_SECTION(.pci_fixup_early, \
hook, vendor, device, class, class_shift, hook)

等宏进行挂载。

  • 举个例子

    arch\x86\pci
static void pci_early_fixup_cyrix_5530(struct pci_dev *dev)
{
u8 r;
/* clear 'F4 Video Configuration Trap' bit */
pci_read_config_byte(dev, 0x42, &r);
r &= 0xfd;
pci_write_config_byte(dev, 0x42, r);
}
//注册回调函数到pci_early_fixup段中,用于修复x86平台下该设备出现的bug。
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY,
pci_early_fixup_cyrix_5530);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY,
pci_early_fixup_cyrix_5530);

通过DECLARE_PCI_FIXUP_EARLY宏将pci_early_fixup_cyrix_5530回调函数注册到pci_fixup_early段中,在通过hook函数指针调用并执行函数解决x86平台下5530设备特有的错误情况。

1.2.10 设置command寄存器

1.2.10.1 错误情况禁止IO空间和内存空间

//没有识别正确的
if (dev->non_compliant_bars) {
pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
pci_info(dev, "device has non-compliant BARs; disabling IO/MEM decoding\n");
cmd &= ~PCI_COMMAND_IO;
cmd &= ~PCI_COMMAND_MEMORY;
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
}

COMMAND[0] :IO SPACE位

该位表示PCI设备是否响应I/O请求,为1时响应,0时不响应。

COMMAND[1]: Memory Space位

该位表示PCI设备是否响应存储器请求,为1时响应,0时不响应。

1.2.10.2 判断command寄存器中断禁止位是否可写

pci_intx_mask_broken()

 u16 orig, toggle, new;

 pci_read_config_word(dev, PCI_COMMAND, &orig);
toggle = orig ^ PCI_COMMAND_INTX_DISABLE;
pci_write_config_word(dev, PCI_COMMAND, toggle);
pci_read_config_word(dev, PCI_COMMAND, &new);
pci_write_config_word(dev, PCI_COMMAND, orig);
//PCI_COMMAND_INTX_DISABLE是预留位并在PCIr2.3版本是只读的,因此严格输出
//如果他是不可写的,则这个设备没有损坏。
if (new != toggle)
return 1;
return 0;

COMMAND[10] :interrupt Disable位

复位值为0,该位为1时,PCI设备不能通过INTx信号向HOST主桥提交中断请求,为0时可以使用INTx信号提出请求。当PCI设备使用MSI中断方式提交中断请求时,该位将被置为1。

1.2.11 根据不同头类型做初始化

1.2.11.1标准头

1.2.11.1.1获取设备信息

获取设备中断信息和BAR空间信息。

switch (dev->hdr_type) {	/* header type */
case PCI_HEADER_TYPE_NORMAL: /* standard header */
//错误情况
if (class == PCI_CLASS_BRIDGE_PCI)
goto bad;
//获取中断号和中断引脚
pci_read_irq(dev);
//获取基地址
pci_read_bases(dev, 6, PCI_ROM_ADDRESS); //获取子厂商号设备号
pci_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device);

(1)中断信息

包括中断号,中断引脚。

pci_read_irq()

 pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq);
dev->pin = irq;
if (irq)
pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
dev->irq = irq;

**(2)BAR空间信息 **

包括BAR空间大小,空间基地址,空间类型(IO/内存),空间位数(32位,64位)等。

pci_read_bases函数调用__pci_read_base函数来获取这些信息。

__pci_read_base()

(2.1)默认为 32位PCI时,获取PCI空间大小

 pci_read_config_dword(dev, pos, &l);
pci_write_config_dword(dev, pos, l | mask);
pci_read_config_dword(dev, pos, &sz);
pci_write_config_dword(dev, pos, l);
  • (2.2) 通过decode_bar()函数判别PCIBAR空间类型*
  • 判断是否IO空间类型。
//判断是否IO空间
if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
flags = bar & ~PCI_BASE_ADDRESS_IO_MASK;
flags |= IORESOURCE_IO;
return flags;
}

通过BAR基地址的bit[0]可判别BAR空间类型为IO空间还是内存空间,1表示IO空间,0表示内存空间。

bit[1,2]则表示了BAR空间为32位还是64位。

bit[3]表示BAR是否支持预取。

其中bit[1,2]=01在linux4.x代码中代表1M这种类型。

  • 判断内存空间位数
//判断内存空间位数
mem_type = bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
switch (mem_type) {
case PCI_BASE_ADDRESS_MEM_TYPE_32:
break;
case PCI_BASE_ADDRESS_MEM_TYPE_1M:
/* 1M mem BAR treated as 32-bit BAR */
break;
case PCI_BASE_ADDRESS_MEM_TYPE_64:
flags |= IORESOURCE_MEM_64;
break;
default:
/* mem unknown type treated as 32-bit BAR */
break;
}
  • 64位的处理
//64位重新获取设备基地址和大小
pci_read_config_dword(dev, pos + 4, &l);
pci_write_config_dword(dev, pos + 4, ~0);
pci_read_config_dword(dev, pos + 4, &sz);
pci_write_config_dword(dev, pos + 4, l);

获取地址和大小后还需判断其是否超过体系所支持的位数,32位下是不能支持64位PCI设备的,PCI空间大小也是不能超过4G的。

  • 测试BAR空间映射是否正确。
 pcibios_bus_to_resource(dev->bus, res, &region);
pcibios_resource_to_bus(dev->bus, &inverted_region, res);

pcibios_bus_to_resource函数用于总线地址转换到资源地址(用于CPU的物理地址)。

pcibios_resource_to_bus执行相反操作,如果,相互转换的值不正确,则不能使用该设备。

1.2.11.1.2 对于ATA控制器的特殊设置

传统模式ATA控制器具有固定地址。且BAR0-3的数据在某些情况下是无效的。

classCode寄存器用于判断设备类别。

#define PCI_CLASS_STORAGE_IDE	0x0101

通过base class=01,subclass =01,可以确定为IDE类型存储器

对于IDE控制器类型的PCI设备,又通过interface寄存器字段进行了细分:

interface:

bit7:确定是否为主IDE设备

bit3:可编程指示器(次通道)

bit2:操作模式(次通道)

bit1:可编程指示器(主通道)

bit0:操作模式(主通道)

根据PCI IDE Controller Specification Revision 1.0文档可知

对于主通道来说,命令寄存器被固定为1f0h-1f7h,其控制块地址为3fh,

次通道命令寄存器地址170h-177h,控制寄存器376h.

  if (class == PCI_CLASS_STORAGE_IDE) {
u8 progif;
pci_read_config_byte(dev,, &progif);
//主通道IDE控制器
if ((progif & 1) == 0) {
region.start = 0x1F0;
region.end = 0x1F7;
res = &dev->resource[0];
res->flags = LEGACY_IO_RESOURCE;
pcibios_bus_to_resource(dev->bus, res, &region);
pci_info(dev, "legacy IDE quirk: reg 0x10: %pR\n",
res);
region.start = 0x3F6;
region.end = 0x3F6;
res = &dev->resource[1];
res->flags = LEGACY_IO_RESOURCE;
pcibios_bus_to_resource(dev->bus, res, &region);
pci_info(dev, "legacy IDE quirk: reg 0x14: %pR\n",
res);
}
//次通道IDE控制器
if ((progif & 4) == 0) {
region.start = 0x170;
region.end = 0x177;
res = &dev->resource[2];
res->flags = LEGACY_IO_RESOURCE;
pcibios_bus_to_resource(dev->bus, res, &region);
pci_info(dev, "legacy IDE quirk: reg 0x18: %pR\n",
res);
region.start = 0x376;
region.end = 0x376;
res = &dev->resource[3];
res->flags = LEGACY_IO_RESOURCE;
pcibios_bus_to_resource(dev->bus, res, &region);
pci_info(dev, "legacy IDE quirk: reg 0x1c: %pR\n",
res);
}
}
break;

1.2.11.2桥头

PCI-PCI桥若要求译码(比如透明桥),桥的编程接口代码必须是0x01

case PCI_HEADER_TYPE_BRIDGE:	/* bridge header */
if (class != PCI_CLASS_BRIDGE_PCI)
goto bad;
pci_read_irq(dev);
dev->transparent = ((dev->class & 0xff) == 1);
pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
//查询是否支持热插拔
set_pcie_hotplug_bridge(dev);
//查找capability中子系统的厂商号和设备号
pos = pci_find_capability(dev, PCI_CAP_ID_SSVID);
if (pos) {
pci_read_config_word(dev, pos + PCI_SSVID_VENDOR_ID, &dev->subsystem_vendor);
pci_read_config_word(dev, pos + PCI_SSVID_DEVICE_ID, &dev->subsystem_device);
}
break;

对于热插拔

set_pcie_hotplug_bridge()

 pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &reg32);
if (reg32 & PCI_EXP_SLTCAP_HPC)
pdev->is_hotplug_bridge = 1;

其中

#define PCI_EXP_SLTCAP	20	/* Slot Capabilities */
#define PCI_EXP_SLTCAP_HPC 0x00000040 /* Hot-Plug Capable */

通过查询pcie_3.0总线规范,可以看到偏移为14h的寄存器中有关于是否支持热插拔的状态位。

bit6 = 1时,表示设备支持热插拔。

1.2.11.2cardBus桥头

 case PCI_HEADER_TYPE_CARDBUS:	/* CardBus bridge header */
if (class != PCI_CLASS_BRIDGE_CARDBUS)
goto bad;
pci_read_irq(dev);
pci_read_bases(dev, 1, 0);
pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device);
break;

结束

pci_setup_device()函数完成了对单个设备的设备和检测,并将获取的信息存取设备结构体中,用于后期具体设备的使用。

pci枚举初始化部分(2)的更多相关文章

  1. pci枚举初始化部分(1)

    基于linux-4.20-rc3源码分析 1 .扫描所有PCI设备并检测,填充设备结构体 static struct pci_dev *pci_scan_device(struct pci_bus * ...

  2. linux PCI设备初始化过程

    linux PCI设备初始化过程 start_kernel->rest_init 这个函数会启动一个核心线程0, 核心线程然后调用init -> do_basic_setup. 然后我们开 ...

  3. 【转】PCI学习笔记

    1.PCI设备编号    每一个PCI device都有其unique PFA(PCI Fcntion Address)    PFA由 bus number.device number.functi ...

  4. 【原创】Linux PCI驱动框架分析(二)

    背 景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本 ...

  5. Swift中如何化简标准库中冗长的类实例初始化代码

    可能有些童鞋并不知道,在Swift中缩写点符号对于任何类型的任何static成员都有效. 我们实际写一个例子看一下: import UIKit class CFoo{ static let share ...

  6. Kotlin 枚举类

    枚举类最基本的用法是实现一个类型安全的枚举. 枚举常量用逗号分隔,每个枚举常量都是一个对象. enum class Color{ RED,BLACK,BLUE,GREEN,WHITE } 枚举初始化 ...

  7. [知乎]老狼:深入PCI与PCIe之二:软件篇

    深入PCI与PCIe之二:软件篇 https://zhuanlan.zhihu.com/p/26244141 我们前一篇文章(深入PCI与PCIe之一:硬件篇 - 知乎专栏)介绍了PCI和PCIe的硬 ...

  8. Swift_初始化

    #Swift_初始化 点击查看源码 初始化结构体 //初始化结构体 func testInitStruct() { //结构体 类中默认方法 struct Size { //宽 var width = ...

  9. java编程思想第五章初始化与清理

    5.1使用构造器确保初始化: 构造器与一般方法一样,但是没有返回值,且其方法名与类名完全相同. 不接受任何参数的构造器成为默认构造器,也叫无参构造器. 5.2 方法重载: 为什么会有方法重载? 构造器 ...

随机推荐

  1. leetcode Ch4-Binary Tree & BFS & Divide/Conquer

    一. 1. Lowest Common Ancestor class Solution { public: TreeNode *lowestCommonAncestor(TreeNode *root, ...

  2. Django路由系统---django重点之url映射分发

    django重点之url映射分发 在全局项目的urls.py中进行子项目的映射,然后在子项目中创建一个urls.py去处理自己项目中的请求,同时也实现了代码的解耦 添加路由分发的原则[全局urls.p ...

  3. Connection to linux server with ORACLE SQL DEVELOPER

    1.Link name is random 2.username and password is database account 3.host name  is ip address  ifconf ...

  4. 自动化测试全套流程(一)-搭建Jenkins环境

    前提 既然要做自动化测试,那我们就做得彻底一些,将整套系统部署在Linux服务器上,在搭建Jenkins环境之前,我已经通过VirtualBox安装了一个CentOS的服务器,搭建Jenkins是基于 ...

  5. 深入浅出SharePoint2010——请假系统无代码篇之权限设计

    首选我们需要区分3个跟权限相关的概念. 权限项目(Permission):最小的权限粒度.比如创建列表项.审批等. 权限级别(Permission Level):权限项目不能直接赋予用户或者用户组,只 ...

  6. System.Buffer 以字节数组(Byte[])操作基元类型数据

    1. Buffer.ByteLength:计算基元类型数组累计有多少字节组成. 该方法结果等于"基元类型字节长度 * 数组长度" , , }; , , }; , , }; Cons ...

  7. 字符串String及字符Char的相关方法

    一.字符串: 1.访问String中的字符: string本身可看作一个Char数组. string s = "hello world"; ; i < s.Length; i ...

  8. 021.10 IO流 打印流

    内容:PrintStream:字节流    PrintWriter:字符流 PrintStream public static void main(String[] args) throws IOEx ...

  9. 015.1 Lock接口

    内容:Lock接口使用步骤,同步生产大白兔奶糖的例子 同步代码块的锁是隐式的,显式容易让我们理解.所以我们使用这个显式的方法,方便理解代码.######实现同步步骤:1.获取锁:lock()2.同步代 ...

  10. 设置python的默认编码方式为utf-8

    在python的Lib\site-packages文件夹下新建一个sitecustomize.py,然后通过sys.getdefaultencoding()获取当前的默认编码 内容为: