概念
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_bridgepci_register_host_bridgepci_alloc_busdevice_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_extendfor (devfn = 0; devfn < 256; devfn += 8) // 针对每个devicenr_devs = pci_scan_slot(bus, devfn);for (fn = 1; fn < 8; fn++) { // 针对每个functionpci_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_extendpci_add_new_bus // 添加 pci buspci_scan_child_bus_extend