linux PCI/PCIe驱动之pci_read_bases的理解
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, ®ion);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的理解相关推荐
- linux pci/pcie驱动
/driver/pci/probe.c /arch/powerpc/kernel/pci_64.c 在pci驱动中pci调用pci_scan_device扫描每个设备的每个功能,当发现该功能存在时(通 ...
- 浅谈Linux PCI设备驱动(一)
要弄清楚Linux PCI设备驱动,首先要明白,所谓的Linux PCI设备驱动实际包括Linux PCI设备驱动和设备本身驱动两部分.不知道读者理不理解这句话,本人觉得这句话很重要,对于PCI.US ...
- 浅谈Linux PCI设备驱动(二)
我们在浅谈Linux PCI设备驱动(一)中(以下简称浅谈(一) )介绍了PCI的配置寄存器组,而Linux PCI初始化就是使用了这些寄存器来进行的.后面我们会举个例子来说明Linux PCI设备驱 ...
- Linux PCI网卡驱动分析
http://www.uplinux.com/shizi/wenxian/4429.html Linux网卡驱动分析 学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难 ...
- linux Pci字符驱动基本加载流程
今天有朋友问我linux系统Pci字符驱动加载流程,简单整理了一下,顺便做个记录. 首先说下需要包含的头文件: 一个完整的字符驱动一般包含下面这些头文件: #include <linux/typ ...
- Linux PCI网卡驱动的详细分析
前言 学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难免让人有心惊胆颤,捉襟见肘的感觉.读Linux网卡驱动也是一样.那长长的源码夹杂着那些我们陌生的变量和符号,望而 ...
- linux PCI设备驱动
1.PCI 简介 1.1 PCI 引脚 为处理数据.寻址.接口控制.仲裁以及系统功能,PC接口作为目标设备的设备至少有47条引脚.作为总线主设备的设备至少有49条引脚.必要的引脚在左边,任选的引脚在右 ...
- linux pci网卡驱动
Linux网卡驱动分析 Linux网卡驱动分析 学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难免让人有心惊胆颤,捉襟见肘的感觉.读Linux网卡驱动也是一样.那长长 ...
- Linux PCI 设备驱动基本框架(一)
Linux将所有外部设备看成是一类特殊文件,称之为"设备文件",如果说系统调用是Linux内核和应用程序之间的接口,那么设备驱动程序则可以看成是 Linux内核与外部设备之间的接口 ...
- linux pci 网卡驱动,linux网络设备驱动_pci网卡
<linux网络设备驱动_pci网卡>由会员分享,可在线阅读,更多相关<linux网络设备驱动_pci网卡(12页珍藏版)>请在技术文库上搜索. 1. LinuxLinux 网 ...
最新文章
- Linux下多线程编程互斥锁和条件变量的简单使用
- 数据备份_「Cassandra实战」Cassandra数据备份
- Cloudflare的HTTP/2优化策略
- leetcode 278. 第一个错误的版本(二分)
- python中怎样创建字典内建函数_python中常用的字典内建函数
- 关于Zookeeper的几个问题
- ES6_解构赋值_note
- python自带sqlite_python内置的sqlite3模块,使用其内置数据库
- jq和thinkphp经常使用的几种ajax
- 算法笔记二分查找问题1
- 简明python教程gitbook_简明Python教程|中英文mobi epub pdf|源代码
- matlab三边定位算法,利用matlab分别对三边测量定位算法和改进算法进行仿真和验证...
- abs在c 语言中的作用,c语言中abs是什么意思
- 零基础搭建Win系统Anaconda+Pytorch+OpenCV深度学习环境(Win10、Win11、RTX 3090显卡也适用)
- 苹果cms首页文件html,苹果cms首页视频不更新怎么解决
- PUK ACM题目分类
- oracle查询当天的数据(当年,当月,当日)
- 使用 EasyExcel 读取和下载 excel 文件
- 数字孪生城市概念分析及建设方案详解
- 【UnityC#】写了一个事件分发器