[patch 2/2] i386/x86_64: collect host bridge resources v2

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch reads and stores host bridge resources reported by
ACPI BIOS for i386 and x86_64 systems.  This is needed since
ACPI hotplug code now uses the PCI core for resource management. 

Signed-off-by: Rajesh Shah <[email protected]>

Index: linux-2.6.12-rc5/arch/i386/pci/acpi.c
===================================================================
--- linux-2.6.12-rc5.orig/arch/i386/pci/acpi.c
+++ linux-2.6.12-rc5/arch/i386/pci/acpi.c
@@ -5,14 +5,165 @@
 #include <asm/hw_irq.h>
 #include "pci.h"
 
+static void inline
+add_resource_range(int idx, struct pci_bus *bus,
+		struct acpi_resource_address64 *addr, unsigned long flags)
+{
+	memset(bus->resource[idx], 0, sizeof(*(bus->resource[idx])));
+	bus->resource[idx]->name = bus->name;
+	bus->resource[idx]->start = addr->min_address_range;
+	bus->resource[idx]->end = addr->max_address_range;
+	bus->resource[idx]->flags = flags;
+}
+
+static acpi_status
+add_resources(struct acpi_resource *acpi_res, void *context)
+{
+	struct acpi_resource_address64 address;
+	struct pci_bus *bus = context;
+	int idx;
+	unsigned long flags = 0;
+	struct resource *busr;
+
+	if (acpi_res->id != ACPI_RSTYPE_ADDRESS16 &&
+	    acpi_res->id != ACPI_RSTYPE_ADDRESS32 &&
+	    acpi_res->id != ACPI_RSTYPE_ADDRESS64)
+		return AE_OK;
+
+	if (ACPI_FAILURE(acpi_resource_to_address64(acpi_res, &address)))
+		return AE_OK;
+
+	/*
+	 * Per the ACPI spec, we should pick up only ACPI_PRODUCER type
+	 * resources. However, many BIOSs get this wrong and report
+	 * resources they pass down as ACPI_CONSUMER type resources. For now,
+	 * pick up all resources here.
+	 */
+	if (address.address_length <= 0)
+		return AE_OK;
+
+	switch (address.resource_type) {
+		case ACPI_IO_RANGE:
+			flags = IORESOURCE_IO;
+			break;
+		case ACPI_MEMORY_RANGE:
+			flags = IORESOURCE_MEM;
+			if (address.attribute.memory.cache_attribute ==
+					ACPI_PREFETCHABLE_MEMORY)
+				flags |= IORESOURCE_PREFETCH;
+			break;
+		default:
+			return AE_OK;
+			break;
+	}
+	for (idx = 0; idx < PCI_BUS_NUM_RESOURCES; idx++) {
+		if (!bus->resource[idx]) {
+			bus->resource[idx] = kmalloc(
+					sizeof(*(bus->resource[idx])),
+					GFP_KERNEL);
+			if (!bus->resource[idx])
+				break;
+			add_resource_range(idx, bus, &address, flags);
+			return AE_OK;
+		}
+		busr = bus->resource[idx];
+		if (busr->flags != flags)
+			continue;
+		/* Consolidate adjacent resource ranges */
+		if (busr->end + 1 == address.min_address_range) {
+			busr->end = address.max_address_range;
+			return AE_OK;
+		}
+		if (address.max_address_range + 1 == busr->start) {
+			busr->start = address.min_address_range;
+			return AE_OK;
+		}
+		/* Consolidate overlapping resource ranges */
+		if (address.max_address_range < busr->start)
+			continue;
+		if (address.min_address_range > busr->end)
+			continue;
+		if (address.min_address_range < busr->start)
+			busr->start = address.min_address_range;
+		if (address.max_address_range > busr->end)
+			busr->end = address.max_address_range;
+		return AE_OK;
+	}
+	printk(KERN_WARNING
+		"PCI: Cannot add host bridge range %Lx-%Lx, type %x\n",
+		address.min_address_range, address.max_address_range,
+		address.resource_type);
+	return AE_ERROR;
+}
+
+static int __devinit
+verify_root_windows(struct pci_bus *bus)
+{
+	int i, num_io = 0, num_mem = 0;
+	int type_mask = IORESOURCE_IO | IORESOURCE_MEM;
+
+	for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
+		if (!bus->resource[i])
+			continue;
+		switch (bus->resource[i]->flags & type_mask) {
+			case IORESOURCE_IO:
+				num_io++;
+				break;
+			case IORESOURCE_MEM:
+				num_mem++;
+				break;
+			default:
+				break;
+		}
+	}
+
+	if (num_io || num_mem)
+		return 1;
+	else
+		printk(KERN_WARNING
+			"PCI: BIOS reported bogus host bridge resources\n");
+	return 0;
+}
+
+static void __devinit
+pcibios_setup_root_windows(struct pci_bus *bus, acpi_handle handle)
+{
+	int i;
+	acpi_status status;
+	struct resource *bres[PCI_BUS_NUM_RESOURCES];
+
+	for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
+		bres[i] = bus->resource[i];
+		bus->resource[i] = NULL;
+	}
+
+	sprintf(bus->name, "PCI Bus #%02x", bus->number);
+	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+			add_resources, bus);
+	if (ACPI_FAILURE(status) || !verify_root_windows(bus)) {
+		printk(KERN_WARNING
+			"PCI: Falling back to default host bridge resources\n");
+		for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
+			kfree(bus->resource[i]);
+			bus->resource[i] = bres[i];
+		}
+	}
+}
+
+
 struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int domain, int busnum)
 {
+	struct pci_bus *bus;
+
 	if (domain != 0) {
 		printk(KERN_WARNING "PCI: Multiple domains not supported\n");
 		return NULL;
 	}
 
-	return pcibios_scan_root(busnum);
+	bus =  pcibios_scan_root(busnum);
+	if (bus) 
+		pcibios_setup_root_windows(bus, device->handle);
+	return bus;
 }
 
 extern int pci_routeirq;

--
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux