diff options
Diffstat (limited to 'arch/x86/pci/i386.c')
| -rw-r--r-- | arch/x86/pci/i386.c | 185 | 
1 files changed, 121 insertions, 64 deletions
diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index dd8ca6f7223..94919e307f8 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 @@ -193,46 +201,46 @@ EXPORT_SYMBOL(pcibios_align_resource);   *	    as well.   */ -static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +static void pcibios_allocate_bridge_resources(struct pci_dev *dev)  { -	struct pci_bus *bus; -	struct pci_dev *dev;  	int idx;  	struct resource *r; -	/* 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; -				} -			} +	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;  		} -		pcibios_allocate_bus_resources(&bus->children);  	}  } +static void pcibios_allocate_bus_resources(struct pci_bus *bus) +{ +	struct pci_bus *child; + +	/* Depth-First Search on bus tree */ +	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 {  	int start;  	int end;  }; -static void __init pcibios_allocate_resources(int pass) +static void pcibios_allocate_dev_resources(struct pci_dev *dev, int pass)  { -	struct pci_dev *dev = NULL;  	int idx, disabled, i;  	u16 command;  	struct resource *r; @@ -244,14 +252,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); @@ -270,44 +277,74 @@ 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 int __init pcibios_assign_resources(void) +static void pcibios_allocate_resources(struct pci_bus *bus, int pass) +{ +	struct pci_dev *dev; +	struct pci_bus *child; + +	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 void 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 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(); @@ -315,12 +352,32 @@ 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; +  	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();  	/*  |