pci设备枚举流程

概念

PCI设备:遵循PCI规范,工作在PCI局部总线环境下的设备。PCI局部总线规范指出,每个PCI设备可以包含最多8个PCI功能,每个PCI功能是一个逻辑设备

PCI桥设备:由于电子负载限制,每条PCI总线上可以挂载的设备数目是有限的因此使用了一种特殊的设备,即PCI-PCI桥设备将两条独立的PCI总线连接起来,PCI-PCI桥设备简称PCI桥

主桥设备:和CPU以及内存连在一起的Host-PCI桥设备为主桥设备,主桥设备引出的总线也称为PCI根总线。

通讯:PCI设备也有自己内存空间和IO空间,这些空间被映射到CPU的内存空间和IO空间,在映射之后,PCI设备上的物理资源“变成”了CPU的本地资源

配置

每个PCI桥设备在接收到配置事务后,判断配置目标是否在它的下游总线。若不是,则忽略该事务;若在它引出的总线上,则在这条局部总线上广播事务,否则向下游传播。而每个PCI事务决定是否认领这个配置事务,通常最终会有一个PCI设备会对这个配置事务做出响应

 配置事务的一般布局

 通过在IO地址空间特定寄存器写总线,设备号,功能,字节号,数据

static int falcon_pcie_hw_wr_cfg(struct falcon_pcie_port *port, u32 bus, u32 devfn,
                              int where, int size, u32 *val)
{
        int ret;
        u32 value;

        mutex_lock(&port->lock);
        if (PCI_FUNC(devfn) == 0) {
                if (bus == 1) {
                        if ((where != CFG_BAR0_REG) && (where != CFG_BAR1_REG)) {
                                value = readl(port->base + PCIE_CFGNUM);
                                writel((value|(0x1<<8)), port->base + PCIE_CFGNUM);
                                ret = cfg_write(port->base + FALCON_PCIE_CONFIG_OFFSET
                                                + (where & ~0x3), where, size, val);
                        }
                } else if (bus == 0){
                        /* avoid CFG_BAR0_REG/CFG_BAR1_REG to be overwritten later */
                        if ((where != CFG_BAR0_REG) && (where != CFG_BAR1_REG)) {
                                value = readl(port->base + PCIE_CFGNUM);
                                writel((value&(~(0x1<<8))), port->base + PCIE_CFGNUM);
                                ret = cfg_write(port->base + FALCON_PCIE_CONFIG_OFFSET
                                        + (where & ~0x3), where, size, val);
                        }
                }
        }

        mutex_unlock(&port->lock);

        return ret;
}

linux的通用配置接口位于drivers/pci/access.c

#define PCI_OP_READ(size, type, len) \
int noinline pci_bus_read_config_##size \
        (struct pci_bus *bus, unsigned int devfn, int pos, type *value) \
{                                                                       \
        int res;                                                        \
        unsigned long flags;                                            \
        u32 data = 0;                                                   \
        if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER;       \
        pci_lock_config(flags);                                         \
        res = bus->ops->read(bus, devfn, pos, len, &data);              \
        *value = (type)data;                                            \
        pci_unlock_config(flags);                                       \
        return res;                                                     \
}

#define PCI_OP_WRITE(size, type, len) \
int noinline pci_bus_write_config_##size \
        (struct pci_bus *bus, unsigned int devfn, int pos, type value)  \
{                                                                       \
        int res;                                                        \
        unsigned long flags;                                            \
        if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER;       \
        pci_lock_config(flags);                                         \
        res = bus->ops->write(bus, devfn, pos, len, value);             \
        pci_unlock_config(flags);                                       \
        return res;                                                     \
}

PCI_OP_READ(byte, u8, 1)
PCI_OP_READ(word, u16, 2)
PCI_OP_READ(dword, u32, 4)
PCI_OP_WRITE(byte, u8, 1)
PCI_OP_WRITE(word, u16, 2)
PCI_OP_WRITE(dword, u32, 4)

EXPORT_SYMBOL(pci_bus_read_config_byte);
EXPORT_SYMBOL(pci_bus_read_config_word);
EXPORT_SYMBOL(pci_bus_read_config_dword);
EXPORT_SYMBOL(pci_bus_write_config_byte);
EXPORT_SYMBOL(pci_bus_write_config_word);
EXPORT_SYMBOL(pci_bus_write_config_dword);

主机驱动

探测函数

直接看探测函数的主要功能如下:

pci_host_bridge_priv将平台相关数据,填充到主机桥的private成员;

platform_set_drvdata再将平台相关数据,填充到device的driver_data成员

devm_pinctrl_get_select_default设置pinctrl为默认状态的设备树节点

pm_qos_add_request设定该控制器对性能的期望

device_init_wakeup将该设备设置成唤醒源,休眠的时候,会将该设备的中断使能唤醒功能

pci_host_probe注册pci主机

device_create_file给设备创建属性文件

static int pcie_probe(struct platform_device *pdev)
{
        struct device *dev = &pdev->dev;
        struct falcon_pcie *pcie;
        struct pci_host_bridge *host;
        int err;

        host = devm_pci_alloc_host_bridge(dev, sizeof(*pcie));
        if (!host)
                return -ENOMEM;

        pcie = pci_host_bridge_priv(host);
        pcie->dev = dev;
        pcie->soc = of_device_get_match_data(dev);

        devm_pinctrl_get_select_default(dev);
        err = falcon_pcie_setup(pcie);
        if (err)
                return -ENODEV;

        platform_set_drvdata(pdev, pcie);
        host->busnr = pcie->busnr;
        host->dev.parent = pcie->dev;
        host->ops = pcie->soc->ops;
        host->map_irq = of_irq_parse_and_map_pci;
        host->swizzle_irq = pci_common_swizzle;
        host->sysdata = pcie;
        pcie->host = host;

        pm_qos_add_request(&pcie->qos_idle, PM_QOS_CPUIDLE_BLOCK,
                        PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
        pcie->qos_idle.name = pdev->name;

        device_init_wakeup(&pdev->dev, 1);
        err = pci_host_probe(host);
        pm_qos_update_request(&pcie->qos_idle, pcie->port->lpm_qos);

        device_create_file(&pdev->dev, &dev_attr_pwr_ctrl);

        if (err)
                goto put_resources;

        return 0;

put_resources:
        falcon_pcie_put_resources(pcie);

        return err;
}

static struct platform_driver pcie_driver = {
        .probe = falcon_pcie_probe,
        .remove = falcon_pcie_remove,
        .driver = {
                .name = "pcie-test",
                .of_match_table = pcie_ids,
                .suppress_bind_attrs = true,
                .pm = &falcon_pcie_pm_ops,
        },
};

/* Falcon PCIe driver does not allow module unload */
static int __init falcon_pcie_init(void)
{
        return platform_driver_probe(&falcon_pcie_driver, falcon_pcie_probe);
}
device_initcall_sync(falcon_pcie_init);

//module_platform_driver(falcon_pcie_driver);

platform_driver_probe

看下面注释和代码:设备注册要先于驱动注册;只做一次探测,不支持延迟探测;一般用于soc的控制器驱动

/**
 * __platform_driver_probe - register driver for non-hotpluggable device
 * @drv: platform driver structure
 * @probe: the driver probe routine, probably from an __init section
 * @module: module which will be the owner of the driver
 *
 * Use this instead of platform_driver_register() when you know the device
 * is not hotpluggable and has already been registered, and you want to
 * remove its run-once probe() infrastructure from memory after the driver
 * has bound to the device.
 *
 * One typical use for this would be with drivers for controllers integrated
 * into system-on-chip processors, where the controller devices have been
 * configured as part of board setup.
 *
 * Note that this is incompatible with deferred probing.
 *
 * Returns zero if the driver registered and bound to a device, else returns
 * a negative error code and with the driver not registered.
 */
int __init_or_module __platform_driver_probe(struct platform_driver *drv,
		int (*probe)(struct platform_device *), struct module *module)
{
	int retval, code;

	if (drv->driver.probe_type == PROBE_PREFER_ASYNCHRONOUS) {
		pr_err("%s: drivers registered with %s can not be probed asynchronously\n",
			 drv->driver.name, __func__);
		return -EINVAL;
	}

	/*
	 * We have to run our probes synchronously because we check if
	 * we find any devices to bind to and exit with error if there
	 * are any.
	 */
	drv->driver.probe_type = PROBE_FORCE_SYNCHRONOUS;

	/*
	 * Prevent driver from requesting probe deferral to avoid further
	 * futile probe attempts.
	 */
	drv->prevent_deferred_probe = true;

	/* make sure driver won't have bind/unbind attributes */
	drv->driver.suppress_bind_attrs = true;

	/* temporary section violation during probe() */
	drv->probe = probe;
	retval = code = __platform_driver_register(drv, module);
	if (retval)
		return retval;

	/*
	 * Fixup that section violation, being paranoid about code scanning
	 * the list of drivers in order to probe new devices.  Check to see
	 * if the probe was successful, and make sure any forced probes of
	 * new devices fail.
	 */
	spin_lock(&drv->driver.bus->p->klist_drivers.k_lock);
	drv->probe = NULL;
	if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
		retval = -ENODEV;
	drv->driver.probe = platform_drv_probe_fail;
	spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock);

	if (code != retval)
		platform_driver_unregister(drv);
	return retval;
}
EXPORT_SYMBOL_GPL(__platform_driver_probe);

pci_host_probe

主机去注册一个主机桥设备和bus 0;然后从这个bus递归扫描设备

pci_host_probe(bridge)
	pci_scan_root_bus_bridge
		pci_register_host_bridge
			pci_alloc_bus
			device_add(&bridge->dev); 	// 添加device(host bridge)
			pcibios_add_bus(bus)  		// 添加bus (local bus 0)
		pci_scan_child_bus/pci_scan_child_bus_extend(bus,0)
	bus = bridge->bus;
	pci_bus_add_devices(bus);

pci_scan_child_bus_extend

先扫描普通设备,扫描就根据设备的配置空间来配置和添加设备;

再扫描桥,扫描到就添加总线。然后继续递归扫描。

pci_scan_child_bus_extend
    for (devfn = 0; devfn < 256; devfn += 8)  // 针对每个device
        nr_devs = pci_scan_slot(bus, devfn);
		for (fn = 1; fn < 8; fn++) { // 针对每个function
            pci_scan_single_device(bus, devfn + fn); // 对一个function 枚举
            	struct pci_dev *dev = pci_scan_device  // 通过配置空间来枚举设备
                    struct pci_dev *dev = pci_alloc_dev(bus);
            		dev->devfn = devfn;
					pci_setup_device(dev)   // 修改基址、数据寄存器,实际下发配置修改
                pci_device_add(dev)			// 添加 device(pci bridge或者普通pci设备)
    pci_scan_bridge_extend
        pci_add_new_bus						// 添加 pci bus
        pci_scan_child_bus_extend

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/619905.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器人学导论实验3-机器人定位中的直线拟合与提取

目录 1 实验目的 2 任务一&#xff1a;直线拟合 2.1 内容分析 2.2 过程分析 2.3 结果分析 3 任务二&#xff1a;直线提取 3.1 内容分析 3.2 过程分析 3.3 结果分析 4 遇到的问题和心得 机器人导论实验-机器人定位中的直线拟合与提取 1 实验目的 2 任务一&#xff1a; 直线…

Dubbo基本使用

Dubbo基本使用 1.项目介绍2.开发步骤2.1 启动注册中心2.2 初始化项目2.3 添加 Maven 依赖2.3.1 父pom.xml2.3.1 consumer模块和provider模块pom.xml 2.4 定义服务接口2.5 定义服务端的实现2.6 配置服务端 Yaml 配置文件2.7 配置消费端 Yaml 配置文件2.8 基于 Spring 配置服务端…

抖音本地团购商家采集软件使用指南

引言&#xff1a; 随着移动互联网的快速发展&#xff0c;抖音成为了一个极为受欢迎的短视频平台。在抖音上存在着大量的本地团购商家&#xff0c;对于一些用户来说&#xff0c;这是一个很好的在线购物平台。但是要想找到适合自己的本地团购商家&#xff0c;需要花费大量的时间和…

2.分布式-算法

目录 一、限流算法有哪些&#xff1f; 1.计数器算法&#xff08;Counter-Based Algorithm&#xff09; 2.固定窗口算法&#xff08;Fixed Window&#xff09; 3.滑动窗口算法&#xff08;Sliding Window&#xff09; 4.令牌桶算法&#xff08;Token Bucket&#xff09; 5.…

Spring底层入门(十一)

1、条件装配 在上一篇中&#xff0c;我们介绍了Spring&#xff0c;Spring MVC常见类的自动装配&#xff0c;在源码中可见许多以Conditional...开头的注解&#xff1a; Conditional 注解是Spring 框架提供的一种条件化装配的机制&#xff0c;它可以根据特定的条件来控制 Bean 的…

Redis 的数据库管理

Redis 提供了⼏个⾯向 Redis 数据库的操作&#xff0c;分别是 dbsize、select、flushdb、flushall 命令&#xff0c; 我将介绍这些常见的命令。 切换数据库 select dbIndex许多关系型数据库&#xff0c;例如 MySQL ⽀持在⼀个实例下有多个数据库存在的&#xff0c;MySQL 可以…

SQLZOO:The JOIN operation

数据表&#xff1a;game-gaol-eteam game idmdatestadiumteam1team210018 June 2012National Stadium, WarsawPOLGRE10028 June 2012Stadion Miejski (Wroclaw)RUSCZE100312 June 2012Stadion Miejski (Wroclaw)GRECZE100412 June 2012National Stadium, WarsawPOLRUS... goal …

mapreduce | 自定义Partition分区(案例2)

1.需求 统计每个手机号消费总金额&#xff0c;按照消费金额降序排序&#xff0c;最终联通、电信、移动分别写入不同的文件。 130、131、132&#xff08;联通&#xff09; 133&#xff08;电信&#xff09; 135、136、137、138、139 &#xff08;移动&#xff09; 手机号,消费记…

卡尔曼滤波状态估计

clear all; close all; clc; %% 上面是调用卡尔曼滤波 % 定义状态维数和初始条件 n 3; % 状态维数 q 0.2; % 过程噪声标准差 r 0.15; % 测量噪声标准差 Q q * eye(n); …

基于JAVA的微信小程序二手车交易平台(源码)

博主介绍&#xff1a;✌程序员徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Redis经典问题:数据并发竞争

大家好,我是小米!今天我们要聊的话题是在大流量系统中常见的一个问题:数据并发竞争。不管是火车票系统还是微博系统,一旦出现数据并发竞争,都可能导致用户体验下降,甚至系统崩溃。那么,我们该如何解决这个问题呢?让我们一起来深入探讨! 数据并发竞争 当我们谈论大流…

LLM - Generate With KV-Cache 图解与实践 By GPT-2

目录 一.引言 二.KV-Cache 图解 1.Attention 计算 2.Generate WithOut KV-Cache 3.Generate With KV-Cache 4.Cache Memory Usage 三.KV-Cache 实践 1.WithOut KV-Cache 2.With KV-Cache 3.Compare Efficiency 四.总结 一.引言 LLM 推理中 KV-Cache 是最常见的优化…

若依-2主1从表(解决了编辑页面的添加按钮失效问题)

1. 3个表的分析&#xff08;表名里不要加“t_”&#xff0c;会出现问题&#xff09; 主表&#xff1a;t_qxk 这是试卷表 主表&#xff1a;t_ques_xk 这是题目表 子表&#xff1a;t_quescxk 这是试卷和题目的关系表&#xff0c;即同时是试卷和题目表的子表。 因为一张试卷可…

给centos机器打个样格式化挂载磁盘(新机器)

文章目录 一、先安装lvm2二、观察磁盘三、磁盘分区四、建PV五、建VG六、创建LV七、在LV上创建文件系统八、挂载到/home&#xff08;1&#xff09;临时挂载&#xff08;2&#xff09;永久挂载 九、最后reboot一下 一、先安装lvm2 yum install lvm2二、观察磁盘 三、磁盘分区 四…

QT 项目打包(为了后期远程实验用)

一、环境准备 1、一个项目工程 二、步骤 1、将编译器设置调整为Release模式 二、对项目重新编译构建 三、可以看到工程目录这个文件夹 打开工程目录文件夹的Release文件夹&#xff0c;我的路径如下 四、新建一个文件夹&#xff0c;将上述路径文件夹下的exe文件复制到新的文…

云相册APP

简介 一款用于云存照片的app&#xff0c;支持批量上传和下载照片。 平台技术 Android客户端&#xff1a;Kotlin 协程 Retrofit Server服务后端&#xff1a;Java SpringBoot 部署云服务器&#xff1a;华为云耀云服务器L实例 下载网址 小鲸鱼相册 Ps: 由于网站域名备案审核…

SQL STRING_SPLIT函数,将指定的分隔符将字符串拆分为子字符串行

文章目录 STRING_SPLIT (Transact-SQL)1、语法2、参数3、样例样例1样例2 STRING_SPLIT (Transact-SQL) STRING_SPLIT 是一个表值函数&#xff0c;它根据指定的分隔符将字符串拆分为子字符串行。 1、语法 STRING_SPLIT ( string , separator [ , enable_ordinal ] ) 2、参数…

ICLR上新 | 强化学习、扩散模型、多模态语言模型,你想了解的前沿方向进展全都有

编者按&#xff1a;欢迎阅读“科研上新”栏目&#xff01;“科研上新”汇聚了微软亚洲研究院最新的创新成果与科研动态。在这里&#xff0c;你可以快速浏览研究院的亮点资讯&#xff0c;保持对前沿领域的敏锐嗅觉&#xff0c;同时也能找到先进实用的开源工具。 今天的“科研上…

AlphaFold3—转录因子预测(实操)

写在前面 我们上一次已经介绍了如何使用AlphaFold3&#xff1a;最新AlphaFold 3&#xff1a;预测所有生物分子结构、相互作用 AlphaFold3可以做什么&#xff1f; 1.AlphaFold服务器可以对以下生物分子类型进行建模&#xff0c;评价其相互结合&#xff1a; 蛋白质 DNA RNA 生…

计算机网络-DHCPv6基础

前面我们学习了IPv6地址可以通过手动配置、无状态自动配置、DHCPv6配置&#xff0c;这里简单学习下DHCPv6的知识点。 一、DHCPv6概述 DHCPv6 (Dynamic Host Configuration Protocol for IPv6) 是一种网络协议&#xff0c;设计用于IPv6网络环境中自动为网络设备分配必要的配置信…