From f7ac356dc3da1f69dc52cb6273e08e53b85b4884 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:24 -0700 Subject: x86/PCI: Factor out pcibios_allocate_bridge_resources() Thus pcibios_allocate_bus_resources() could more simple and clean. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) (limited to 'arch/x86/pci') diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index dd8ca6f7223..98003628690 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -193,34 +193,36 @@ EXPORT_SYMBOL(pcibios_align_resource); * as well. */ -static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +static void __init pcibios_allocate_bridge_resources(struct pci_dev *dev) { - struct pci_bus *bus; - struct pci_dev *dev; int idx; struct resource *r; + for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { + r = &dev->resource[idx]; + if (!r->flags) + continue; + if (!r->start || pci_claim_resource(dev, idx) < 0) { + /* + * Something is wrong with the region. + * Invalidate the resource to prevent + * child resource allocations in this + * range. + */ + r->start = r->end = 0; + r->flags = 0; + } + } +} + +static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct pci_bus *bus; + /* Depth-First Search on bus tree */ list_for_each_entry(bus, bus_list, node) { - if ((dev = bus->self)) { - for (idx = PCI_BRIDGE_RESOURCES; - idx < PCI_NUM_RESOURCES; idx++) { - r = &dev->resource[idx]; - if (!r->flags) - continue; - if (!r->start || - pci_claim_resource(dev, idx) < 0) { - /* - * Something is wrong with the region. - * Invalidate the resource to prevent - * child resource allocations in this - * range. - */ - r->start = r->end = 0; - r->flags = 0; - } - } - } + if (bus->self) + pcibios_allocate_bridge_resources(bus->self); pcibios_allocate_bus_resources(&bus->children); } } -- cgit v1.2.3-70-g09d2 From c7f4bbc92feee2986212ef3b42c806e2257197dc Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:25 -0700 Subject: x86/PCI: Factor out pcibios_allocate_dev_resources() Factor pcibios_allocate_dev_resources() out of pcibios_allocate_resources(). Currently we only allocate these resources at boot-time with a for_each_pci_dev() loop. Eventually we'll use pcibios_allocate_dev_resources() for hot-added devices, too. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) (limited to 'arch/x86/pci') diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 98003628690..5817cf235d9 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -232,9 +232,8 @@ struct pci_check_idx_range { int end; }; -static void __init pcibios_allocate_resources(int pass) +static void __init pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) { - struct pci_dev *dev = NULL; int idx, disabled, i; u16 command; struct resource *r; @@ -246,14 +245,13 @@ static void __init pcibios_allocate_resources(int pass) #endif }; - for_each_pci_dev(dev) { - pci_read_config_word(dev, PCI_COMMAND, &command); - for (i = 0; i < ARRAY_SIZE(idx_range); i++) + pci_read_config_word(dev, PCI_COMMAND, &command); + for (i = 0; i < ARRAY_SIZE(idx_range); i++) for (idx = idx_range[i].start; idx <= idx_range[i].end; idx++) { r = &dev->resource[idx]; - if (r->parent) /* Already allocated */ + if (r->parent) /* Already allocated */ continue; - if (!r->start) /* Address not assigned at all */ + if (!r->start) /* Address not assigned at all */ continue; if (r->flags & IORESOURCE_IO) disabled = !(command & PCI_COMMAND_IO); @@ -272,23 +270,29 @@ static void __init pcibios_allocate_resources(int pass) } } } - if (!pass) { - r = &dev->resource[PCI_ROM_RESOURCE]; - if (r->flags & IORESOURCE_ROM_ENABLE) { - /* Turn the ROM off, leave the resource region, - * but keep it unregistered. */ - u32 reg; - dev_dbg(&dev->dev, "disabling ROM %pR\n", r); - r->flags &= ~IORESOURCE_ROM_ENABLE; - pci_read_config_dword(dev, - dev->rom_base_reg, ®); - pci_write_config_dword(dev, dev->rom_base_reg, + if (!pass) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & IORESOURCE_ROM_ENABLE) { + /* Turn the ROM off, leave the resource region, + * but keep it unregistered. */ + u32 reg; + dev_dbg(&dev->dev, "disabling ROM %pR\n", r); + r->flags &= ~IORESOURCE_ROM_ENABLE; + pci_read_config_dword(dev, dev->rom_base_reg, ®); + pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE); - } } } } +static void __init pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev = NULL; + + for_each_pci_dev(dev) + pcibios_allocate_dev_resources(dev, pass); +} + static int __init pcibios_assign_resources(void) { struct pci_dev *dev = NULL; -- cgit v1.2.3-70-g09d2 From 83edc87ce8b284a3d60ab8072e55041c76a68277 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:26 -0700 Subject: x86/PCI: Allocate resources on a per-bus basis for hot-adding root buses Previously pcibios_allocate_resources() allocated resources at boot-time for all PCI devices using for_each_pci_dev(). This patch changes pcibios_allocate_resources() so we can specify a bus, so we can do similar allocation when hot-adding a root bus. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) (limited to 'arch/x86/pci') diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 5817cf235d9..84696ed1f0e 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -215,16 +215,15 @@ static void __init pcibios_allocate_bridge_resources(struct pci_dev *dev) } } -static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +static void __init pcibios_allocate_bus_resources(struct pci_bus *bus) { - struct pci_bus *bus; + struct pci_bus *child; /* Depth-First Search on bus tree */ - list_for_each_entry(bus, bus_list, node) { - if (bus->self) - pcibios_allocate_bridge_resources(bus->self); - pcibios_allocate_bus_resources(&bus->children); - } + if (bus->self) + pcibios_allocate_bridge_resources(bus->self); + list_for_each_entry(child, &bus->children, node) + pcibios_allocate_bus_resources(child); } struct pci_check_idx_range { @@ -285,12 +284,18 @@ static void __init pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) } } -static void __init pcibios_allocate_resources(int pass) +static void __init pcibios_allocate_resources(struct pci_bus *bus, int pass) { - struct pci_dev *dev = NULL; + struct pci_dev *dev; + struct pci_bus *child; - for_each_pci_dev(dev) + list_for_each_entry(dev, &bus->devices, bus_list) { pcibios_allocate_dev_resources(dev, pass); + + child = dev->subordinate; + if (child) + pcibios_allocate_resources(child, pass); + } } static int __init pcibios_assign_resources(void) @@ -323,10 +328,17 @@ static int __init pcibios_assign_resources(void) void __init pcibios_resource_survey(void) { + struct pci_bus *bus; + DBG("PCI: Allocating resources\n"); - pcibios_allocate_bus_resources(&pci_root_buses); - pcibios_allocate_resources(0); - pcibios_allocate_resources(1); + + list_for_each_entry(bus, &pci_root_buses, node) + pcibios_allocate_bus_resources(bus); + + list_for_each_entry(bus, &pci_root_buses, node) + pcibios_allocate_resources(bus, 0); + list_for_each_entry(bus, &pci_root_buses, node) + pcibios_allocate_resources(bus, 1); e820_reserve_resources_late(); /* -- cgit v1.2.3-70-g09d2 From dc2f56fa8400677ef4852d5128f03b795cf57e7b Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:27 -0700 Subject: x86/PCI: Factor out pcibios_allocate_dev_rom_resource() Factor pcibios_allocate_rom_resources() and pcibios_allocate_dev_rom_resource() out of pcibios_assign_resources(). This will allow us to allocate ROM resources for hot-added root buses. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 52 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 17 deletions(-) (limited to 'arch/x86/pci') diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 84696ed1f0e..42dd7555235 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -298,27 +298,45 @@ static void __init pcibios_allocate_resources(struct pci_bus *bus, int pass) } } -static int __init pcibios_assign_resources(void) +static void __init pcibios_allocate_dev_rom_resource(struct pci_dev *dev) { - struct pci_dev *dev = NULL; struct resource *r; - if (!(pci_probe & PCI_ASSIGN_ROMS)) { - /* - * Try to use BIOS settings for ROMs, otherwise let - * pci_assign_unassigned_resources() allocate the new - * addresses. - */ - for_each_pci_dev(dev) { - r = &dev->resource[PCI_ROM_RESOURCE]; - if (!r->flags || !r->start) - continue; - if (pci_claim_resource(dev, PCI_ROM_RESOURCE) < 0) { - r->end -= r->start; - r->start = 0; - } - } + /* + * Try to use BIOS settings for ROMs, otherwise let + * pci_assign_unassigned_resources() allocate the new + * addresses. + */ + r = &dev->resource[PCI_ROM_RESOURCE]; + if (!r->flags || !r->start) + return; + + if (pci_claim_resource(dev, PCI_ROM_RESOURCE) < 0) { + r->end -= r->start; + r->start = 0; } +} +static void __init pcibios_allocate_rom_resources(struct pci_bus *bus) +{ + struct pci_dev *dev; + struct pci_bus *child; + + list_for_each_entry(dev, &bus->devices, bus_list) { + pcibios_allocate_dev_rom_resource(dev); + + child = dev->subordinate; + if (child) + pcibios_allocate_rom_resources(child); + } +} + +static int __init pcibios_assign_resources(void) +{ + struct pci_bus *bus; + + if (!(pci_probe & PCI_ASSIGN_ROMS)) + list_for_each_entry(bus, &pci_root_buses, node) + pcibios_allocate_rom_resources(bus); pci_assign_unassigned_resources(); pcibios_fw_addr_list_del(); -- cgit v1.2.3-70-g09d2 From 745216025de0354eea23493d994e3fc0ab7369fc Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:28 -0700 Subject: x86/PCI: Don't track firmware-assigned BAR values for hot-added devices The BIOS doesn't assign BAR values for hot-added devices, so don't bother saving the original values when we enumerate these devices. [bhelgaas: changelog, return constant 0 in pcibios_retrieve_fw_addr] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'arch/x86/pci') diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 42dd7555235..1bd672ab008 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -51,6 +51,7 @@ struct pcibios_fwaddrmap { static LIST_HEAD(pcibios_fwaddrmappings); static DEFINE_SPINLOCK(pcibios_fwaddrmap_lock); +static bool pcibios_fw_addr_done; /* Must be called with 'pcibios_fwaddrmap_lock' lock held. */ static struct pcibios_fwaddrmap *pcibios_fwaddrmap_lookup(struct pci_dev *dev) @@ -72,6 +73,9 @@ pcibios_save_fw_addr(struct pci_dev *dev, int idx, resource_size_t fw_addr) unsigned long flags; struct pcibios_fwaddrmap *map; + if (pcibios_fw_addr_done) + return; + spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags); map = pcibios_fwaddrmap_lookup(dev); if (!map) { @@ -97,6 +101,9 @@ resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx) struct pcibios_fwaddrmap *map; resource_size_t fw_addr = 0; + if (pcibios_fw_addr_done) + return 0; + spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags); map = pcibios_fwaddrmap_lookup(dev); if (map) @@ -106,7 +113,7 @@ resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx) return fw_addr; } -static void pcibios_fw_addr_list_del(void) +static void __init pcibios_fw_addr_list_del(void) { unsigned long flags; struct pcibios_fwaddrmap *entry, *next; @@ -118,6 +125,7 @@ static void pcibios_fw_addr_list_del(void) kfree(entry); } spin_unlock_irqrestore(&pcibios_fwaddrmap_lock, flags); + pcibios_fw_addr_done = true; } static int -- cgit v1.2.3-70-g09d2 From b95168e010a405add13aa010d7c45b55dc4026c7 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:29 -0700 Subject: x86/PCI: Keep resource allocation functions after boot The PCI resource allocation functions will be used for hot-added devices, so keep them around. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'arch/x86/pci') diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 1bd672ab008..8656ea88cf6 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -201,7 +201,7 @@ EXPORT_SYMBOL(pcibios_align_resource); * as well. */ -static void __init pcibios_allocate_bridge_resources(struct pci_dev *dev) +static void pcibios_allocate_bridge_resources(struct pci_dev *dev) { int idx; struct resource *r; @@ -223,7 +223,7 @@ static void __init pcibios_allocate_bridge_resources(struct pci_dev *dev) } } -static void __init pcibios_allocate_bus_resources(struct pci_bus *bus) +static void pcibios_allocate_bus_resources(struct pci_bus *bus) { struct pci_bus *child; @@ -239,7 +239,7 @@ struct pci_check_idx_range { int end; }; -static void __init pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) +static void pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) { int idx, disabled, i; u16 command; @@ -292,7 +292,7 @@ static void __init pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) } } -static void __init pcibios_allocate_resources(struct pci_bus *bus, int pass) +static void pcibios_allocate_resources(struct pci_bus *bus, int pass) { struct pci_dev *dev; struct pci_bus *child; @@ -306,7 +306,7 @@ static void __init pcibios_allocate_resources(struct pci_bus *bus, int pass) } } -static void __init pcibios_allocate_dev_rom_resource(struct pci_dev *dev) +static void pcibios_allocate_dev_rom_resource(struct pci_dev *dev) { struct resource *r; @@ -324,7 +324,7 @@ static void __init pcibios_allocate_dev_rom_resource(struct pci_dev *dev) r->start = 0; } } -static void __init pcibios_allocate_rom_resources(struct pci_bus *bus) +static void pcibios_allocate_rom_resources(struct pci_bus *bus) { struct pci_dev *dev; struct pci_bus *child; -- cgit v1.2.3-70-g09d2 From b3e65e1f9185a2eb034defe4270ba178ba70b9a9 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:30 -0700 Subject: x86/PCI: Implement pcibios_resource_survey_bus() During testing remove/rescan root bus 00, found [ 338.142574] bus: 'pci': really_probe: probing driver ata_piix with device 0000:00:01.1 [ 338.146788] ata_piix 0000:00:01.1: device not available (can't reserve [io 0x01f0-0x01f7]) [ 338.150565] ata_piix: probe of 0000:00:01.1 failed with error -22 because that fixed resource is not claimed. For bootint path it is claimed in from arch/x86/pci/i386.c::pcibios_allocate_resources() Claim those resources, so on the remove/rescan will still use old resources. It is some kind honoring FW setting in the registers during hot add. esp root-bus hot add is through acpi, BIOS has chance to set some registers before handing over. [bhelgaas: move weak definition to patch that uses it] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'arch/x86/pci') diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 8656ea88cf6..94919e307f8 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -352,6 +352,19 @@ static int __init pcibios_assign_resources(void) return 0; } +void pcibios_resource_survey_bus(struct pci_bus *bus) +{ + dev_printk(KERN_DEBUG, &bus->dev, "Allocating resources\n"); + + pcibios_allocate_bus_resources(bus); + + pcibios_allocate_resources(bus, 0); + pcibios_allocate_resources(bus, 1); + + if (!(pci_probe & PCI_ASSIGN_ROMS)) + pcibios_allocate_rom_resources(bus); +} + void __init pcibios_resource_survey(void) { struct pci_bus *bus; -- cgit v1.2.3-70-g09d2