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

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值