概念
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