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. SQL Server 2014 虚拟机的自动备份 (Resource Manager)

    自动备份将在运行 SQL Server 2014 Standard 或 Enterprise 的 Azure VM 上自动为所有现有数据库和新数据库配置托管备份到 Azure. 这样,便可以配置使用持 ...

  2. 爬虫入门之scrapy模拟登陆(十四)

    注意:模拟登陆时,必须保证settings.py里的COOKIES_ENABLED(Cookies中间件) 处于开启状态 COOKIES_ENABLED = True或# COOKIES_ENABLE ...

  3. 3 Dockerfile指令详解-FROM&MAINTAINER&RUN

    1.FROM指令 FROM centos #指定centos为基础镜像 2.MAINTAINER 指令 MAINTAINER @QQ.COM #指定维护人等信息,方便维护 3.RUN  命令  #新建 ...

  4. 套接字和标准I/O缓冲区

    设置标准I/O函数缓冲区的主要目的是为了提高性能.但套接字中的缓冲主要是为了实现TCP协议而设立的.例如,TCP传输中丢失数据时将再次传递,而再次发送数据则意味着在某地保存了数据.存在什么地方呢?套接 ...

  5. December 24th 2016 Week 52nd Saturday

    The first step is as good as half over. 第一步是最关键的一步. If one goes wrong at the first steps, what shoul ...

  6. scala简介

    1.什么是Scala scala官方网址: http://www.scala-lang.org Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性.Scala运行 ...

  7. python的*args和**kwargs基础用法

    *args表示任何多个无名参数,它是一个tuple **kwargs:传入的字典,就如:a=1,传入键值,默认就传入到**kwargs中,如下面代码: class FOO: def __init__( ...

  8. CString char BSTR 转换

     关于字符集不一的历史原因,可以参考: UNICODE与ANSI的区别 以下是网上转载的资料.我将辅以自己的实例,说明并总结关系. 一.CString, int, string, char*之间的转换 ...

  9. JavaScript权威指南第01章 JavaScript 概述

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/huangbin10025/article/details/27951767 JavaScript 概 ...

  10. POJ 1743 Musical Theme 【后缀数组 最长不重叠子串】

    题目冲鸭:http://poj.org/problem?id=1743 Musical Theme Time Limit: 1000MS   Memory Limit: 30000K Total Su ...