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. Python实例---简单的选课系统

    要求 思路: 构造方法传递过去学校名称,同时利用UUID创建一个随机字符串,用这个字符串来作为要写入的文件名 利用类的__str__方法来实现类对象接口返回学校名称的操作 利用pickle的dumps ...

  3. 如何在Code First、Database First和Model First之间选择

    Code First.Database First和Model First基本图解: 1)Database First: 如果数据库已经存在,可以使用VS自动生成数据模型,已经相关的edmx信息 2) ...

  4. 纯绿色集成环境,可切换180个Mysql、700个PHP版本

    测试神器又出新版!功能更强大(目测linux版本也快要出了,拭目以待吧) PHPWAMP8.8.8.8集成环境,目测大概更新如下内容(我也就是大略看了下更新内容) 1.支持自定义设置任意Mysql版本 ...

  5. requirejs 多页面,多js 打包代码,requirejs多对多打包

    这段代码来自 http://stackoverflow.com/questions/20583812/grunt-requirejs-optimizer-for-a-multi-app-project ...

  6. php懈垢windows通用上传缺陷

    #1 实例介绍本案例采用的实例是:U-Mail邮件系统.U-Mail邮件系统文件上传的地方代码是这样的: code 区域 <?php if(ACTION =="attach-uploa ...

  7. linux性能系列--内存

    一.啥是内存呢? 回答:内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁.计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大. 内存(Memory)也被称为内存储器, ...

  8. CSS加载性能优化

    将首屏页面要用到的CSS文件,放在页面头部加载,其他模块的CSS可以使用异步加载:loadCSS 和 Preload. 关于preload,推进2篇文章给大家看下: 1.通过rel="pre ...

  9. django创建项目问题解决办法

    问题描述: Python编程:从入门到实践第十八章18.1.6 ❶(ll_env)learning_log$ django-admin.py startproject learning_log .❷ ...

  10. 数据类型.md

    数据类型 整型 数据类型 含义(有符号) tinyint(m) 1个字节 范围(-128~127) smallint(m) 2个字节 范围(-32768~32767) mediumint(m) 3个字 ...