PCIe学习笔记之pcie初始化枚举和资源分配流程代码分析_linux pcie bar空间初始化代码-CSDN博客
subsys_initcall(acpi_init);
acpi_init
static int __init acpi_init(void)
{
int result;
if (acpi_disabled) {
printk(KERN_INFO PREFIX "Interpreter disabled.\n");
return -ENODEV;
}
acpi_kobj = kobject_create_and_add("acpi", firmware_kobj);
if (!acpi_kobj) {
printk(KERN_WARNING "%s: kset create error\n", __func__);
acpi_kobj = NULL;
}
result = acpi_bus_init();
if (result) {
kobject_put(acpi_kobj);
disable_acpi();
return result;
}
pci_mmcfg_late_init();
acpi_iort_init();
acpi_scan_init();
acpi_ec_init();
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
acpi_debugger_init();
acpi_setup_sb_notify_handler();
return 0;
}
acpi_scan_init
int __init acpi_scan_init(void)
{
int result;
acpi_status status;
struct acpi_table_stao *stao_ptr;
acpi_pci_root_init();
acpi_pci_link_init();
acpi_processor_init();
acpi_platform_init();
acpi_lpss_init();
acpi_apd_init();
acpi_cmos_rtc_init();
acpi_container_init();
acpi_memory_hotplug_init();
acpi_watchdog_init();
acpi_pnp_init();
acpi_int340x_thermal_init();
acpi_amba_init();
acpi_init_lpit();
acpi_scan_add_handler(&generic_device_handler);
/*
* If there is STAO table, check whether it needs to ignore the UART
* device in SPCR table.
*/
status = acpi_get_table(ACPI_SIG_STAO, 0,
(struct acpi_table_header **)&stao_ptr);
if (ACPI_SUCCESS(status)) {
if (stao_ptr->header.length > sizeof(struct acpi_table_stao))
printk(KERN_INFO PREFIX "STAO Name List not yet supported.");
if (stao_ptr->ignore_uart)
acpi_get_spcr_uart_addr();
}
acpi_gpe_apply_masked_gpes();
acpi_update_all_gpes();
/*
* Although we call __add_memory() that is documented to require the
* device_hotplug_lock, it is not necessary here because this is an
* early code when userspace or any other code path cannot trigger
* hotplug/hotunplug operations.
*/
mutex_lock(&acpi_scan_lock);
/*
* Enumerate devices in the ACPI namespace.
*/
result = acpi_bus_scan(ACPI_ROOT_OBJECT);
if (result)
goto out;
result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &acpi_root);
if (result)
goto out;
/* Fixed feature devices do not exist on HW-reduced platform */
if (!acpi_gbl_reduced_hardware) {
result = acpi_bus_scan_fixed();
if (result) {
acpi_detach_data(acpi_root->handle,
acpi_scan_drop_device);
acpi_device_del(acpi_root);
put_device(&acpi_root->dev);
goto out;
}
}
acpi_scan_initialized = true;
out:
mutex_unlock(&acpi_scan_lock);
return result;
}
acpi_pci_root_init
void __init acpi_pci_root_init(void)
{
acpi_hest_init();
if (acpi_pci_disabled)
return;
pci_acpi_crs_quirks();
acpi_scan_add_handler_with_hotplug(&pci_root_handler, "pci_root");
}
pci_root_handler
static struct acpi_scan_handler pci_root_handler = {
.ids = root_device_ids,
.attach = acpi_pci_root_add,
.detach = acpi_pci_root_remove,
.hotplug = {
.enabled = true,
.scan_dependent = acpi_pci_root_scan_dependent,
},
};
android/kernel/msm-5.4/drivers/acpi/pci_root.c
acpi_pci_root_add
static int acpi_pci_root_add(struct acpi_device *device,
const struct acpi_device_id *not_used)
{
unsigned long long segment, bus;
acpi_status status;
int result;
struct acpi_pci_root *root;
acpi_handle handle = device->handle;
int no_aspm = 0;
bool hotadd = system_state == SYSTEM_RUNNING;
bool is_pcie;
root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL);
if (!root)
return -ENOMEM;
segment = 0;
status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL,
&segment);
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
dev_err(&device->dev, "can't evaluate _SEG\n");
result = -ENODEV;
goto end;
}
/* Check _CRS first, then _BBN. If no _BBN, default to zero. */
root->secondary.flags = IORESOURCE_BUS;
status = try_get_root_bridge_busnr(handle, &root->secondary);
if (ACPI_FAILURE(status)) {
/*
* We need both the start and end of the downstream bus range
* to interpret _CBA (MMCONFIG base address), so it really is
* supposed to be in _CRS. If we don't find it there, all we
* can do is assume [_BBN-0xFF] or [0-0xFF].
*/
root->secondary.end = 0xFF;
dev_warn(&device->dev,
FW_BUG "no secondary bus range in _CRS\n");
status = acpi_evaluate_integer(handle, METHOD_NAME__BBN,
NULL, &bus);
if (ACPI_SUCCESS(status))
root->secondary.start = bus;
else if (status == AE_NOT_FOUND)
root->secondary.start = 0;
else {
dev_err(&device->dev, "can't evaluate _BBN\n");
result = -ENODEV;
goto end;
}
}
root->device = device;
root->segment = segment & 0xFFFF;
strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS);
device->driver_data = root;
if (hotadd && dmar_device_add(handle)) {
result = -ENXIO;
goto end;
}
pr_info(PREFIX "%s [%s] (domain %04x %pR)\n",
acpi_device_name(device), acpi_device_bid(device),
root->segment, &root->secondary);
root->mcfg_addr = acpi_pci_root_get_mcfg_addr(handle);
is_pcie = strcmp(acpi_device_hid(device), "PNP0A08") == 0;
negotiate_os_control(root, &no_aspm, is_pcie);
/*
* TBD: Need PCI interface for enumeration/configuration of roots.
*/
/*
* Scan the Root Bridge
* --------------------
* Must do this prior to any attempt to bind the root device, as the
* PCI namespace does not get created until this call is made (and
* thus the root bridge's pci_dev does not exist).
*/
root->bus = pci_acpi_scan_root(root);
if (!root->bus) {
dev_err(&device->dev,
"Bus %04x:%02x not present in PCI namespace\n",
root->segment, (unsigned int)root->secondary.start);
device->driver_data = NULL;
result = -ENODEV;
goto remove_dmar;
}
if (no_aspm)
pcie_no_aspm();
pci_acpi_add_bus_pm_notifier(device);
device_set_wakeup_capable(root->bus->bridge, device->wakeup.flags.valid);
if (hotadd) {
pcibios_resource_survey_bus(root->bus);
pci_assign_unassigned_root_bus_resources(root->bus);
/*
* This is only called for the hotadd case. For the boot-time
* case, we need to wait until after PCI initialization in
* order to deal with IOAPICs mapped in on a PCI BAR.
*
* This is currently x86-specific, because acpi_ioapic_add()
* is an empty function without CONFIG_ACPI_HOTPLUG_IOAPIC.
* And CONFIG_ACPI_HOTPLUG_IOAPIC depends on CONFIG_X86_IO_APIC
* (see drivers/acpi/Kconfig).
*/
acpi_ioapic_add(root->device->handle);
}
pci_lock_rescan_remove();
pci_bus_add_devices(root->bus);
pci_unlock_rescan_remove();
return 1;
remove_dmar:
if (hotadd)
dmar_device_remove(handle);
end:
kfree(root);
return result;
}
LINUX/android/kernel/msm-5.4/arch/arm64/kernel/pci.c
pci_acpi_scan_root
pci_acpi_scan_root, pcie枚举流程的入口
/* Interface called from ACPI code to setup PCI host controller */
struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
{
struct acpi_pci_generic_root_info *ri;
struct pci_bus *bus, *child;
struct acpi_pci_root_ops *root_ops;
struct pci_host_bridge *host;
ri = kzalloc(sizeof(*ri), GFP_KERNEL);
if (!ri)
return NULL;
root_ops = kzalloc(sizeof(*root_ops), GFP_KERNEL);
if (!root_ops) {
kfree(ri);
return NULL;
}
ri->cfg = pci_acpi_setup_ecam_mapping(root);
if (!ri->cfg) {
kfree(ri);
kfree(root_ops);
return NULL;
}
root_ops->release_info = pci_acpi_generic_release_info;
root_ops->prepare_resources = pci_acpi_root_prepare_resources;
//获取对应芯片平台的pci_ops
root_ops->pci_ops = &ri->cfg->ops->pci_ops;
bus = acpi_pci_root_create(root, root_ops, &ri->common, ri->cfg);
if (!bus)
return NULL;
/* If we must preserve the resource configuration, claim now */
host = pci_find_host_bridge(bus);
if (host->preserve_config)
pci_bus_claim_resources(bus);
/*
* Assign whatever was left unassigned. If we didn't claim above,
* this will reassign everything.
*/
pci_assign_unassigned_root_bus_resources(bus);
list_for_each_entry(child, &bus->children, node)
pcie_bus_configure_settings(child);
return bus;
}
pci_acpi_setup_ecam_mapping
/*
* Lookup the bus range for the domain in MCFG, and set up config space
* mapping.
*/
static struct pci_config_window *
pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
{
struct device *dev = &root->device->dev;
struct resource *bus_res = &root->secondary;
u16 seg = root->segment;
struct pci_ecam_ops *ecam_ops;
struct resource cfgres;
struct acpi_device *adev;
struct pci_config_window *cfg;
int ret;
ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops);
if (ret) {
dev_err(dev, "%04x:%pR ECAM region not found\n", seg, bus_res);
return NULL;
}
adev = acpi_resource_consumer(&cfgres);
if (adev)
dev_info(dev, "ECAM area %pR reserved by %s\n", &cfgres,
dev_name(&adev->dev));
else
dev_warn(dev, FW_BUG "ECAM area %pR not reserved in ACPI namespace\n",
&cfgres);
cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops);
if (IS_ERR(cfg)) {
dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res,
PTR_ERR(cfg));
return NULL;
}
return cfg;
}
pci_mcfg_lookup
int pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres,
struct pci_ecam_ops **ecam_ops)
{
struct pci_ecam_ops *ops = &pci_generic_ecam_ops;
struct resource *bus_res = &root->secondary;
u16 seg = root->segment;
struct mcfg_entry *e;
struct resource res;
/* Use address from _CBA if present, otherwise lookup MCFG */
if (root->mcfg_addr)
goto skip_lookup;
/*
* We expect the range in bus_res in the coverage of MCFG bus range.
*/
list_for_each_entry(e, &pci_mcfg_list, list) {
if (e->segment == seg && e->bus_start <= bus_res->start &&
e->bus_end >= bus_res->end) {
root->mcfg_addr = e->addr;
}
}
skip_lookup:
memset(&res, 0, sizeof(res));
if (root->mcfg_addr) {
res.start = root->mcfg_addr + (bus_res->start << 20);
res.end = res.start + (resource_size(bus_res) << 20) - 1;
res.flags = IORESOURCE_MEM;
}
/*
* Allow quirks to override default ECAM ops and CFG resource
* range. This may even fabricate a CFG resource range in case
* MCFG does not have it. Invalid CFG start address means MCFG
* firmware bug or we need another quirk in array.
*/
pci_mcfg_apply_quirks(root, &res, &ops);
if (!res.start)
return -ENXIO;
*cfgres = res;
*ecam_ops = ops;
return 0;
}
pci_mcfg_apply_quirks
static void pci_mcfg_apply_quirks(struct acpi_pci_root *root,
struct resource *cfgres,
struct pci_ecam_ops **ecam_ops)
{
#ifdef CONFIG_PCI_QUIRKS
u16 segment = root->segment;
struct resource *bus_range = &root->secondary;
struct mcfg_fixup *f;
int i;
for (i = 0, f = mcfg_quirks; i < ARRAY_SIZE(mcfg_quirks); i++, f++) {
if (pci_mcfg_quirk_matches(f, segment, bus_range)) {
if (f->cfgres.start)
*cfgres = f->cfgres;
if (f->ops)
*ecam_ops = f->ops;
dev_info(&root->device->dev, "MCFG quirk: ECAM at %pR for %pR with %ps\n",
cfgres, bus_range, *ecam_ops);
return;
}
}
#endif
}
mcfg_quirks
pcie 对rc操作的ops_host 是 rc吗-CSDN博客
rc 的全程是root compose,一般有host bridage + host bus + 几个 host port组成,是最靠近cpu的pci device。对rc的操作有一个专门的ops,每家的都不一样。这个ops 一般实在pci_mcfg_match_quirks 中根据bios传递过来的mcfg_oem_id/mcfg_oem_table_id/mcfg_oem_revision 来决定。
kernel中所支持的ops都在mcfg_quirks中
原文链接:https://blog.csdn.net/tiantao2012/article/details/65934961
static struct mcfg_fixup mcfg_quirks[] = {
/* { OEM_ID, OEM_TABLE_ID, REV, SEGMENT, BUS_RANGE, ops, cfgres }, */
#ifdef CONFIG_ARM64
#define AL_ECAM(table_id, rev, seg, ops) \
{ "AMAZON", table_id, rev, seg, MCFG_BUS_ANY, ops }
AL_ECAM("GRAVITON", 0, 0, &al_pcie_ops),
AL_ECAM("GRAVITON", 0, 1, &al_pcie_ops),
AL_ECAM("GRAVITON", 0, 2, &al_pcie_ops),
AL_ECAM("GRAVITON", 0, 3, &al_pcie_ops),
AL_ECAM("GRAVITON", 0, 4, &al_pcie_ops),
AL_ECAM("GRAVITON", 0, 5, &al_pcie_ops),
AL_ECAM("GRAVITON", 0, 6, &al_pcie_ops),
AL_ECAM("GRAVITON", 0, 7, &al_pcie_ops),
#define QCOM_ECAM32(seg) \
{ "QCOM ", "QDF2432 ", 1, seg, MCFG_BUS_ANY, &pci_32b_ops }
QCOM_ECAM32(0),
QCOM_ECAM32(1),
QCOM_ECAM32(2),
QCOM_ECAM32(3),
QCOM_ECAM32(4),
QCOM_ECAM32(5),
QCOM_ECAM32(6),
QCOM_ECAM32(7),
#define HISI_QUAD_DOM(table_id, seg, ops) \
{ "HISI ", table_id, 0, (seg) + 0, MCFG_BUS_ANY, ops }, \
{ "HISI ", table_id, 0, (seg) + 1, MCFG_BUS_ANY, ops }, \
{ "HISI ", table_id, 0, (seg) + 2, MCFG_BUS_ANY, ops }, \
{ "HISI ", table_id, 0, (seg) + 3, MCFG_BUS_ANY, ops }
HISI_QUAD_DOM("HIP05 ", 0, &hisi_pcie_ops),
HISI_QUAD_DOM("HIP06 ", 0, &hisi_pcie_ops),
HISI_QUAD_DOM("HIP07 ", 0, &hisi_pcie_ops),
HISI_QUAD_DOM("HIP07 ", 4, &hisi_pcie_ops),
HISI_QUAD_DOM("HIP07 ", 8, &hisi_pcie_ops),
HISI_QUAD_DOM("HIP07 ", 12, &hisi_pcie_ops),
#define THUNDER_PEM_RES(addr, node) \
DEFINE_RES_MEM((addr) + ((u64) (node) << 44), 0x39 * SZ_16M)
#define THUNDER_PEM_QUIRK(rev, node) \
{ "CAVIUM", "THUNDERX", rev, 4 + (10 * (node)), MCFG_BUS_ANY, \
&thunder_pem_ecam_ops, THUNDER_PEM_RES(0x88001f000000UL, node) }, \
{ "CAVIUM", "THUNDERX", rev, 5 + (10 * (node)), MCFG_BUS_ANY, \
&thunder_pem_ecam_ops, THUNDER_PEM_RES(0x884057000000UL, node) }, \
{ "CAVIUM", "THUNDERX", rev, 6 + (10 * (node)), MCFG_BUS_ANY, \
&thunder_pem_ecam_ops, THUNDER_PEM_RES(0x88808f000000UL, node) }, \
{ "CAVIUM", "THUNDERX", rev, 7 + (10 * (node)), MCFG_BUS_ANY, \
&thunder_pem_ecam_ops, THUNDER_PEM_RES(0x89001f000000UL, node) }, \
{ "CAVIUM", "THUNDERX", rev, 8 + (10 * (node)), MCFG_BUS_ANY, \
&thunder_pem_ecam_ops, THUNDER_PEM_RES(0x894057000000UL, node) }, \
{ "CAVIUM", "THUNDERX", rev, 9 + (10 * (node)), MCFG_BUS_ANY, \
&thunder_pem_ecam_ops, THUNDER_PEM_RES(0x89808f000000UL, node) }
#define THUNDER_ECAM_QUIRK(rev, seg) \
{ "CAVIUM", "THUNDERX", rev, seg, MCFG_BUS_ANY, \
&pci_thunder_ecam_ops }
/* SoC pass2.x */
THUNDER_PEM_QUIRK(1, 0),
THUNDER_PEM_QUIRK(1, 1),
THUNDER_ECAM_QUIRK(1, 10),
/* SoC pass1.x */
THUNDER_PEM_QUIRK(2, 0), /* off-chip devices */
THUNDER_PEM_QUIRK(2, 1), /* off-chip devices */
THUNDER_ECAM_QUIRK(2, 0),
THUNDER_ECAM_QUIRK(2, 1),
THUNDER_ECAM_QUIRK(2, 2),
THUNDER_ECAM_QUIRK(2, 3),
THUNDER_ECAM_QUIRK(2, 10),
THUNDER_ECAM_QUIRK(2, 11),
THUNDER_ECAM_QUIRK(2, 12),
THUNDER_ECAM_QUIRK(2, 13),
#define XGENE_V1_ECAM_MCFG(rev, seg) \
{"APM ", "XGENE ", rev, seg, MCFG_BUS_ANY, \
&xgene_v1_pcie_ecam_ops }
#define XGENE_V2_ECAM_MCFG(rev, seg) \
{"APM ", "XGENE ", rev, seg, MCFG_BUS_ANY, \
&xgene_v2_pcie_ecam_ops }
/* X-Gene SoC with v1 PCIe controller */
XGENE_V1_ECAM_MCFG(1, 0),
XGENE_V1_ECAM_MCFG(1, 1),
XGENE_V1_ECAM_MCFG(1, 2),
XGENE_V1_ECAM_MCFG(1, 3),
XGENE_V1_ECAM_MCFG(1, 4),
XGENE_V1_ECAM_MCFG(2, 0),
XGENE_V1_ECAM_MCFG(2, 1),
XGENE_V1_ECAM_MCFG(2, 2),
XGENE_V1_ECAM_MCFG(2, 3),
XGENE_V1_ECAM_MCFG(2, 4),
/* X-Gene SoC with v2.1 PCIe controller */
XGENE_V2_ECAM_MCFG(3, 0),
XGENE_V2_ECAM_MCFG(3, 1),
/* X-Gene SoC with v2.2 PCIe controller */
XGENE_V2_ECAM_MCFG(4, 0),
XGENE_V2_ECAM_MCFG(4, 1),
XGENE_V2_ECAM_MCFG(4, 2),
#define ALTRA_ECAM_QUIRK(rev, seg) \
{ "Ampere", "Altra ", rev, seg, MCFG_BUS_ANY, &pci_32b_read_ops }
ALTRA_ECAM_QUIRK(1, 0),
ALTRA_ECAM_QUIRK(1, 1),
ALTRA_ECAM_QUIRK(1, 2),
ALTRA_ECAM_QUIRK(1, 3),
ALTRA_ECAM_QUIRK(1, 4),
ALTRA_ECAM_QUIRK(1, 5),
ALTRA_ECAM_QUIRK(1, 6),
ALTRA_ECAM_QUIRK(1, 7),
ALTRA_ECAM_QUIRK(1, 8),
ALTRA_ECAM_QUIRK(1, 9),
ALTRA_ECAM_QUIRK(1, 10),
ALTRA_ECAM_QUIRK(1, 11),
ALTRA_ECAM_QUIRK(1, 12),
ALTRA_ECAM_QUIRK(1, 13),
ALTRA_ECAM_QUIRK(1, 14),
ALTRA_ECAM_QUIRK(1, 15),
#endif /* ARM64 */
};
#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
/* ECAM ops for 32-bit access only (non-compliant) */
struct pci_ecam_ops pci_32b_ops = {
.bus_shift = 20,
.pci_ops = {
.map_bus = pci_ecam_map_bus,
.read = pci_generic_config_read32,
.write = pci_generic_config_write32,
}
};
/* ECAM ops for 32-bit read only (non-compliant) */
struct pci_ecam_ops pci_32b_read_ops = {
.bus_shift = 20,
.pci_ops = {
.map_bus = pci_ecam_map_bus,
.read = pci_generic_config_read32,
.write = pci_generic_config_write,
}
};
#endif
acpi_pci_root_create
1.枚举过程
1.1 acpi_pci_root_add
1.2 pci_acpi_scan_root(枚举开始)
1.3 acpi_pci_root_create
1.4 pci_scan_child_bus(枚举执行的重点函数)
1.5 pci_scan_slot(pci_scan_single_device才是做事的)
1.6 pci_scan_device
1.7 pci_device_add
1.8 pci_scan_bridge
原文链接:https://blog.csdn.net/u013253075/article/details/123301127
struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
struct acpi_pci_root_ops *ops,
struct acpi_pci_root_info *info,
void *sysdata)
{
int ret, busnum = root->secondary.start;
struct acpi_device *device = root->device;
int node = acpi_get_node(device->handle);
struct pci_bus *bus;
struct pci_host_bridge *host_bridge;
union acpi_object *obj;
info->root = root;
info->bridge = device;
info->ops = ops;
INIT_LIST_HEAD(&info->resources);
snprintf(info->name, sizeof(info->name), "PCI Bus %04x:%02x",
root->segment, busnum);
if (ops->init_info && ops->init_info(info))
goto out_release_info;
if (ops->prepare_resources)
ret = ops->prepare_resources(info);
else
ret = acpi_pci_probe_root_resources(info);
if (ret < 0)
goto out_release_info;
pci_acpi_root_add_resources(info);
pci_add_resource(&info->resources, &root->secondary);
//注册pci_ops操作接口
bus = pci_create_root_bus(NULL, busnum, ops->pci_ops,
sysdata, &info->resources);
if (!bus)
goto out_release_info;
host_bridge = to_pci_host_bridge(bus->bridge);
if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
host_bridge->native_pcie_hotplug = 0;
if (!(root->osc_control_set & OSC_PCI_SHPC_NATIVE_HP_CONTROL))
host_bridge->native_shpc_hotplug = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_AER_CONTROL))
host_bridge->native_aer = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL))
host_bridge->native_pme = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_LTR_CONTROL))
host_bridge->native_ltr = 0;
/*
* Evaluate the "PCI Boot Configuration" _DSM Function. If it
* exists and returns 0, we must preserve any PCI resource
* assignments made by firmware for this host bridge.
*/
obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), &pci_acpi_dsm_guid, 1,
IGNORE_PCI_BOOT_CONFIG_DSM, NULL);
if (obj && obj->type == ACPI_TYPE_INTEGER && obj->integer.value == 0)
host_bridge->preserve_config = 1;
ACPI_FREE(obj);
//开始枚举设备
pci_scan_child_bus(bus);
pci_set_host_bridge_release(host_bridge, acpi_pci_root_release_info,
info);
if (node != NUMA_NO_NODE)
dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node);
return bus;
out_release_info:
__acpi_pci_root_release_info(info);
return NULL;
}
pci_create_root_bus
struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
struct pci_ops *ops, void *sysdata, struct list_head *resources)
{
int error;
struct pci_host_bridge *bridge;
bridge = pci_alloc_host_bridge(0);
if (!bridge)
return NULL;
bridge->dev.parent = parent;
list_splice_init(resources, &bridge->windows);
bridge->sysdata = sysdata;
bridge->busnr = bus;
//注册bridge ops = pci_ops
bridge->ops = ops;
//注册bus ops
error = pci_register_host_bridge(bridge);
if (error < 0)
goto err_out;
return bridge->bus;
err_out:
put_device(&bridge->dev);
return NULL;
}
EXPORT_SYMBOL_GPL(pci_create_root_bus);
pci_register_host_bridge
static int pci_register_host_bridge(struct pci_host_bridge *bridge)
{
struct device *parent = bridge->dev.parent;
struct resource_entry *window, *n;
struct pci_bus *bus, *b;
resource_size_t offset;
LIST_HEAD(resources);
struct resource *res;
char addr[64], *fmt;
const char *name;
int err;
bus = pci_alloc_bus(NULL);
if (!bus)
return -ENOMEM;
bridge->bus = bus;
/* Temporarily move resources off the list */
list_splice_init(&bridge->windows, &resources);
bus->sysdata = bridge->sysdata;
bus->msi = bridge->msi;
//注册pcie bus ops
bus->ops = bridge->ops;
bus->number = bus->busn_res.start = bridge->busnr;
#ifdef CONFIG_PCI_DOMAINS_GENERIC
bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
#endif
b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
if (b) {
/* Ignore it if we already got here via a different bridge */
dev_dbg(&b->dev, "bus already known\n");
err = -EEXIST;
goto free;
}
dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(bus),
bridge->busnr);
err = pcibios_root_bridge_prepare(bridge);
if (err)
goto free;
err = device_add(&bridge->dev);
if (err) {
put_device(&bridge->dev);
goto free;
}
bus->bridge = get_device(&bridge->dev);
device_enable_async_suspend(bus->bridge);
pci_set_bus_of_node(bus);
pci_set_bus_msi_domain(bus);
if (!parent)
set_dev_node(bus->bridge, pcibus_to_node(bus));
bus->dev.class = &pcibus_class;
bus->dev.parent = bus->bridge;
dev_set_name(&bus->dev, "%04x:%02x", pci_domain_nr(bus), bus->number);
name = dev_name(&bus->dev);
err = device_register(&bus->dev);
if (err)
goto unregister;
pcibios_add_bus(bus);
/* Create legacy_io and legacy_mem files for this bus */
pci_create_legacy_files(bus);
if (parent)
dev_info(parent, "PCI host bridge to bus %s\n", name);
else
pr_info("PCI host bridge to bus %s\n", name);
/* Add initial resources to the bus */
resource_list_for_each_entry_safe(window, n, &resources) {
list_move_tail(&window->node, &bridge->windows);
offset = window->offset;
res = window->res;
if (res->flags & IORESOURCE_BUS)
pci_bus_insert_busn_res(bus, bus->number, res->end);
else
pci_bus_add_resource(bus, res, 0);
if (offset) {
if (resource_type(res) == IORESOURCE_IO)
fmt = " (bus address [%#06llx-%#06llx])";
else
fmt = " (bus address [%#010llx-%#010llx])";
snprintf(addr, sizeof(addr), fmt,
(unsigned long long)(res->start - offset),
(unsigned long long)(res->end - offset));
} else
addr[0] = '\0';
dev_info(&bus->dev, "root bus resource %pR%s\n", res, addr);
}
down_write(&pci_bus_sem);
list_add_tail(&bus->node, &pci_root_buses);
up_write(&pci_bus_sem);
return 0;
unregister:
put_device(&bridge->dev);
device_del(&bridge->dev);
free:
kfree(bus);
return err;
}
pci_scan_child_bus
pci_scan_bridge_extend
pci_scan_child_bus_extend
pci_scan_single_device
PCIe设备枚举的软件实现
1. 设备的扫描从pci_scan_root_bus_bridge开始,首先需要先向系统注册一个host bridge,在注册的过程中需要创建一个root bus,也就是bus 0,在pci_register_host_bridge函数中,主要是一系列的初始化和注册工作,此外还为总线分配资源,包括地址空间等;
2. pci_scan_child_bus开始,从bus 0向下扫描并添加设备,这个过程由pci_scan_child_bus_extend来完成;
3. 从pci_scan_child_bus_extend的流程可以看出,主要有两大块:
• PCI设备扫描,从循环也能看出来,每条总线支持32个设备,每个设备支持8个功能,扫描完设备后将设备注册进系统,pci_scan_device的过程中会去读取PCI设备的配置空间,获取BAR的相关信息;
• PCI桥设备扫描,PCI桥是用于连接上一级PCI总线和下一级PCI总线的,当发现有下一级总线时,创建子结构,并再次调用pci_scan_child_bus_extend的函数来扫描下一级的总线,从这个过程看,就是一个递归过程。
原文链接:https://blog.csdn.net/relax33/article/details/128182253
以RK3568为例,枚举完成后的topology如图:
各种数据结构之前的关系为:
PCIe地址空间
PCIe地址空间包含三类:Cofiguration配置空间;Memory空间;IO空间
注: PCIe spec规定,IO地址空间只为兼容早期的PCI设备,在新设计中应当使用MMIO(Memory Mapped IO),设备中的内部存储和寄存器都统一映射到存储地址空间(Memory Address Space)
每个PCIe设备(endpoint、bridge)都包含一个配置空间(4k), 对于endpoint设备配置空间header为Type0,包含6个32位的BAR(Base Address Register)寄存器,对于bridge设备配置空间header为Type1,包含2个32位的BAR寄存器。通过BAR寄存器可以分别映射PCIe的memory空间和IO空间到系统的设备地址空间和系统的IO空间中。
QCOM QCS8250 PCIe地址空间
reg = <0x60100000 0x10000>表示配置空间
ranges: <local_addr cpu_addr size>
• local_addr:字节数由所在节点的#address-cells决定; 此处为3,local_addr的第一个数字右移24位与0x03作与表示地址空间类型,如
(0x01000000>>24)&0x3==0x01表示IO地址空间;
(0x02000000>>24)&0x3==0x02(32位) or 0x03(64位)表示MEM地址空间.
• cpu_addr:字节数由父节点的#address-cells决定
• size:字节数由所在节点的#size-cells决定
上述ranges的含义为:
PCIe IO地址空间0x60200000---0x602FFFFF映射到CPU地址空间0x60200000---0x602FFFFF,size:1MB
PCIe MEM地址空间0x60300000---0x63FFFFFF映射到CPU地址空间0x60300000---0x63FFFFFF,size:61MB
注: bus-range和ranges地址信息加入到(struct pci_host_bridge)bridge->windows链表中管理
BARs寄存器初始化流程
以32位Endpoint设备请求2MB NP-MMIO示例
1. BAR[0:3]:只读位,含义如下:
bit0:0 = Memory request;1 = IO request
bit[1:2]: 00 = 32-bit decoding; 10 = 64-bit decoding
bit[3]: 0 = non-prefetchable; 1= prefetchable
2. 软件写全1到BAR寄存器,然后读回BAR寄存器的值,2的最低可写位次方就是BAR空间需要的大小,此处最低可写位为21,则BAR大小2^21=2MB
3. 系统为设备分配2MB的地址空间,然后将其实地址写入到BAR寄存器中,此处写到BAR寄存器的值为0x60400000
CPU写该endpoint BAR0寄存器示意图如下:
原文链接:https://blog.csdn.net/relax33/article/details/128182253