linux PCI/PCIe驱动之pci_read_bases的理解

  • 1 pci_read_bases
    • 1.1 pci_read_bases的调用流程
    • 1.2 pci_read_bases函数定义
    • 1.3 __pci_read_base
    • 1.4 pcibios_bus_to_resource

1 pci_read_bases

设备枚举的流程如下所示,在pci_read_bases函数中会去读取PCIe设备的相关信息,主要是涉及到PCI/PCIe设备所需要的资源大小信息。

1.1 pci_read_bases的调用流程

    pci_scan_root_bus(&pdev->dev, 0, &rockchip_pcie_ops, rockchip, &res);pci_scan_root_bus_msipci_scan_child_buspci_scan_slotdev = pci_scan_single_device(bus, devfn);dev = pci_scan_device(bus, devfn);struct pci_dev *dev;dev = pci_alloc_dev(bus);pci_setup_devicepci_read_bases(dev, 6, PCI_ROM_ADDRESS);  pci_device_add(dev, bus);

1.2 pci_read_bases函数定义

  • pci_read_bases首先会依次遍历howmany PCI/PCIe设备的BAR配置
  • struct resource *res = &dev->resource[pos]获取对应的PCI/PCIe设备对应的BAR的resource结构体,为后面的获取到资源信息做填充使用
  • __pci_read_base用于具体的PCI/PCIe设备资源获取和解析,稍后会进一步解析
  • 如果rom是有效的,则会进一步去解析PCI_ROM_RESOURCE的扩展配置空间的资源信息( #6: expansion ROM resource )
static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
{unsigned int pos, reg;if (dev->non_compliant_bars)return;/* Per PCIe r4.0, sec 9.3.4.1.11, the VF BARs are all RO Zero */if (dev->is_virtfn)return;for (pos = 0; pos < howmany; pos++) {struct resource *res = &dev->resource[pos];reg = PCI_BASE_ADDRESS_0 + (pos << 2);pos += __pci_read_base(dev, pci_bar_unknown, res, reg);}if (rom) {struct resource *res = &dev->resource[PCI_ROM_RESOURCE];dev->rom_base_reg = rom;res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH |IORESOURCE_READONLY | IORESOURCE_SIZEALIGN;__pci_read_base(dev, pci_bar_mem32, res, rom);}
}

1.3 __pci_read_base

  • 和函数本身的声明的含义一致,读取PCI/PCIe设备的BAR寄存器,解析PCI/PCIe设备的属性以及资源需求信息
  • 读取BAR寄存器的过程
    • 读BAR,保留原值
    • 写0xFFFFFFFF到BAR
    • 在读出来,解析出所需要的地址空间大小,记录在pci_dev->resource[ ]里
      • pci_dev->resource[ ].start = 0;
      • pci_dev->resource[ ].end = size - 1;
    • 将原值写会BAR寄存器中
  • 根据上一步读取出的l和sz去计算设备当前BAR所需要资源的基址和大小,通过pcibios_bus_to_resource配置到对应设备BAR的resource里面,待稍后的资源分配使用
/*** pci_read_base - Read a PCI BAR* @dev: the PCI device* @type: type of the BAR* @res: resource buffer to be filled in* @pos: BAR position in the config space** Returns 1 if the BAR is 64-bit, or 0 if 32-bit.*/
int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,struct resource *res, unsigned int pos)
{u32 l = 0, sz = 0, mask;u64 l64, sz64, mask64;u16 orig_cmd;struct pci_bus_region region, inverted_region;pci_bus_type mask = type ? PCI_ROM_ADDRESS_MASK : ~0;/* No printks while decoding is disabled! */if (!dev->mmio_always_on) {pci_read_config_word(dev, PCI_COMMAND, &orig_cmd);if (orig_cmd & PCI_COMMAND_DECODE_ENABLE) {pci_write_config_word(dev, PCI_COMMAND,orig_cmd & ~PCI_COMMAND_DECODE_ENABLE);}}res->name = pci_name(dev);/* 读取BAR寄存器的过程 */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);/** All bits set in sz means the device isn't working properly.* If the BAR isn't implemented, all bits must be 0.  If it's a* memory BAR or a ROM, bit 0 must be clear; if it's an io BAR, bit* 1 must be clear.*/if (sz == 0xffffffff)sz = 0;/** I don't know how l can have all bits set.  Copied from old code.* Maybe it fixes a bug on some ancient platform.*/if (l == 0xffffffff)l = 0;/* 对于初始不知道是什么类型PCI/PCIe 设备的,采用的是pci_bar_unknown的配置 */if (type == pci_bar_unknown) {res->flags = decode_bar(dev, l);res->flags |= IORESOURCE_SIZEALIGN;/* 对于IO类型的设备的配置解析 */if (res->flags & IORESOURCE_IO) {/* l64表示数据原始配置信息,sz64表示的是IO设备需要的资源大小 */l64 = l & PCI_BASE_ADDRESS_IO_MASK;sz64 = sz & PCI_BASE_ADDRESS_IO_MASK;mask64 = PCI_BASE_ADDRESS_IO_MASK & (u32)IO_SPACE_LIMIT;} else {/* 对于MEM设备l64表示数据原始配置信息,sz64表示的是MEM设备需要的资源大小 */l64 = l & PCI_BASE_ADDRESS_MEM_MASK;sz64 = sz & PCI_BASE_ADDRESS_MEM_MASK;mask64 = (u32)PCI_BASE_ADDRESS_MEM_MASK;}} else {/* 对于其他已知类型的设备则直接走该流程 */if (l & PCI_ROM_ADDRESS_ENABLE)res->flags |= IORESOURCE_ROM_ENABLE;l64 = l & PCI_ROM_ADDRESS_MASK;sz64 = sz & PCI_ROM_ADDRESS_MASK;mask64 = PCI_ROM_ADDRESS_MASK;}/* 对于MEM64的设备,两个相邻的BAR组成一个对应的64位的有效地址,该处所读取出的配置作为对应的64位地址的高位,而MEM大小也作为高位 */if (res->flags & IORESOURCE_MEM_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);l64 |= ((u64)l << 32);sz64 |= ((u64)sz << 32);mask64 |= ((u64)~0 << 32);}if (!dev->mmio_always_on && (orig_cmd & PCI_COMMAND_DECODE_ENABLE))pci_write_config_word(dev, PCI_COMMAND, orig_cmd);if (!sz64)goto fail;/* 按照对齐的要求去调整PCI/PCIe设备的大小 */sz64 = pci_size(l64, sz64, mask64);if (!sz64) {pci_info(dev, FW_BUG "reg 0x%x: invalid BAR (can't size)\n",pos);goto fail;}if (res->flags & IORESOURCE_MEM_64) {if ((sizeof(pci_bus_addr_t) < 8 || sizeof(resource_size_t) < 8)&& sz64 > 0x100000000ULL) {res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;res->start = 0;res->end = 0;pci_err(dev, "reg 0x%x: can't handle BAR larger than 4GB (size %#010llx)\n",pos, (unsigned long long)sz64);goto out;}if ((sizeof(pci_bus_addr_t) < 8) && l) {/* Above 32-bit boundary; try to reallocate */res->flags |= IORESOURCE_UNSET;res->start = 0;res->end = sz64 - 1;pci_info(dev, "reg 0x%x: can't handle BAR above 4GB (bus address %#010llx)\n",pos, (unsigned long long)l64);goto out;}}region.start = l64;region.end = l64 + sz64 - 1;pcibios_bus_to_resource(dev->bus, res, &region);pcibios_resource_to_bus(dev->bus, &inverted_region, res);/** If "A" is a BAR value (a bus address), "bus_to_resource(A)" is* the corresponding resource address (the physical address used by* the CPU.  Converting that resource address back to a bus address* should yield the original BAR value:**     resource_to_bus(bus_to_resource(A)) == A** If it doesn't, CPU accesses to "bus_to_resource(A)" will not* be claimed by the device.*/if (inverted_region.start != region.start) {res->flags |= IORESOURCE_UNSET;res->start = 0;res->end = region.end - region.start;pci_info(dev, "reg 0x%x: initial BAR value %#010llx invalid\n",pos, (unsigned long long)region.start);}goto out;fail:res->flags = 0;
out:if (res->flags)pci_info(dev, "reg 0x%x: %pR\n", pos, res);return (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
}

1.4 pcibios_bus_to_resource

  • 在该函数中所使用的的bridge->windows所对应的资源数据都是在pci_parse_request_of_pci_ranges函数在初始化过程中解析出来的,在ARM体系结构中由于一般情况下PCI域地址和处理器域地址是一致的,所以通常情况下window->offset的值也为0
  • res->start = region->start + offset和res->end = region->end + offset在稍后的资源分配中需要用到,作为资源分配的依据。
void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res,struct pci_bus_region *region)
{struct pci_host_bridge *bridge = pci_find_host_bridge(bus);struct resource_entry *window;resource_size_t offset = 0;resource_list_for_each_entry(window, &bridge->windows) {struct pci_bus_region bus_region;if (resource_type(res) != resource_type(window->res))continue;bus_region.start = window->res->start - window->offset;bus_region.end = window->res->end - window->offset;if (region_contains(&bus_region, region)) {offset = window->offset;break;}}res->start = region->start + offset;res->end = region->end + offset;
}

linux PCI/PCIe驱动之pci_read_bases的理解相关推荐

  1. linux pci/pcie驱动

    /driver/pci/probe.c /arch/powerpc/kernel/pci_64.c 在pci驱动中pci调用pci_scan_device扫描每个设备的每个功能,当发现该功能存在时(通 ...

  2. 浅谈Linux PCI设备驱动(一)

    要弄清楚Linux PCI设备驱动,首先要明白,所谓的Linux PCI设备驱动实际包括Linux PCI设备驱动和设备本身驱动两部分.不知道读者理不理解这句话,本人觉得这句话很重要,对于PCI.US ...

  3. 浅谈Linux PCI设备驱动(二)

    我们在浅谈Linux PCI设备驱动(一)中(以下简称浅谈(一) )介绍了PCI的配置寄存器组,而Linux PCI初始化就是使用了这些寄存器来进行的.后面我们会举个例子来说明Linux PCI设备驱 ...

  4. Linux PCI网卡驱动分析

    http://www.uplinux.com/shizi/wenxian/4429.html Linux网卡驱动分析 学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难 ...

  5. linux Pci字符驱动基本加载流程

    今天有朋友问我linux系统Pci字符驱动加载流程,简单整理了一下,顺便做个记录. 首先说下需要包含的头文件: 一个完整的字符驱动一般包含下面这些头文件: #include <linux/typ ...

  6. Linux PCI网卡驱动的详细分析

    前言 学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难免让人有心惊胆颤,捉襟见肘的感觉.读Linux网卡驱动也是一样.那长长的源码夹杂着那些我们陌生的变量和符号,望而 ...

  7. linux PCI设备驱动

    1.PCI 简介 1.1 PCI 引脚 为处理数据.寻址.接口控制.仲裁以及系统功能,PC接口作为目标设备的设备至少有47条引脚.作为总线主设备的设备至少有49条引脚.必要的引脚在左边,任选的引脚在右 ...

  8. linux pci网卡驱动

    Linux网卡驱动分析 Linux网卡驱动分析 学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难免让人有心惊胆颤,捉襟见肘的感觉.读Linux网卡驱动也是一样.那长长 ...

  9. Linux PCI 设备驱动基本框架(一)

    Linux将所有外部设备看成是一类特殊文件,称之为"设备文件",如果说系统调用是Linux内核和应用程序之间的接口,那么设备驱动程序则可以看成是 Linux内核与外部设备之间的接口 ...

  10. linux pci 网卡驱动,linux网络设备驱动_pci网卡

    <linux网络设备驱动_pci网卡>由会员分享,可在线阅读,更多相关<linux网络设备驱动_pci网卡(12页珍藏版)>请在技术文库上搜索. 1. LinuxLinux 网 ...

最新文章

  1. Linux下多线程编程互斥锁和条件变量的简单使用
  2. 数据备份_「Cassandra实战」Cassandra数据备份
  3. Cloudflare的HTTP/2优化策略
  4. leetcode 278. 第一个错误的版本(二分)
  5. python中怎样创建字典内建函数_python中常用的字典内建函数
  6. 关于Zookeeper的几个问题
  7. ES6_解构赋值_note
  8. python自带sqlite_python内置的sqlite3模块,使用其内置数据库
  9. jq和thinkphp经常使用的几种ajax
  10. 算法笔记二分查找问题1
  11. 简明python教程gitbook_简明Python教程|中英文mobi epub pdf|源代码
  12. matlab三边定位算法,利用matlab分别对三边测量定位算法和改进算法进行仿真和验证...
  13. abs在c 语言中的作用,c语言中abs是什么意思
  14. 零基础搭建Win系统Anaconda+Pytorch+OpenCV深度学习环境(Win10、Win11、RTX 3090显卡也适用)
  15. 苹果cms首页文件html,苹果cms首页视频不更新怎么解决
  16. PUK ACM题目分类
  17. oracle查询当天的数据(当年,当月,当日)
  18. 使用 EasyExcel 读取和下载 excel 文件
  19. 数字孪生城市概念分析及建设方案详解
  20. 【UnityC#】写了一个事件分发器

热门文章

  1. 腾讯加入“三月宕机全家桶”:系上海网络运营商光纤故障
  2. 把脉城市交通 共商缓堵良策
  3. 3.26 文字工具的使用 [原创Ps教程]
  4. 如何批量将PNG格式转化为JPG格式
  5. 微信消息记录导出到电脑
  6. 算法岗和开发岗有什么区别?
  7. 北京-京医通-小孩-人脸识别
  8. 关于 Linux 中 signal 函数信号处理的讨论
  9. 编译原理 实验四 LR(1)分析法程序
  10. 宁芝84静电容(蓝牙双模)键盘说明书