aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorGary Hade <garyhade@us.ibm.com>2007-10-03 18:56:51 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-10-12 18:03:18 -0400
commit62f420f828249f686aaae949ac3439d1304a759a (patch)
tree0cc534ad4971b9155a23b46c43597d5b5dd9d548 /arch
parentfd64cb4606cbdd592b7119e82341d4ae5b56f2cc (diff)
PCI: use _CRS for PCI resource allocation
Use _CRS for PCI resource allocation This patch resolves an issue where incorrect PCI memory and i/o ranges are being assigned to hotplugged PCI devices on some IBM systems. The resource mis-allocation not only makes the PCI device unuseable but often makes the entire system unuseable due to resulting machine checks. The hotplug capable PCI slots on the affected systems are not located under a standard P2P bridge but are instead located under PCI root bridges or subtractive decode P2P bridges. For example, the IBM x3850 contains 2 hotplug capable PCI-X slots and 4 hotplug capable PCIe slots with the PCI-X slots each located under a PCI root bridge and the PCIe slots each located under a subtractive decode P2P bridge. The current i386/x86_64 PCI resource allocation code does not use _CRS returned resource information. No other resource information source is available for slots that are not below a standard P2P bridge so incorrect ranges are being allocated from e820 hole causing the bad result. This patch causes the kernel to use _CRS returned resource info. It is roughly based on a change provided by Matthew Wilcox for the ia64 kernel in 2005. Due to possible buggy BIOS factor and possible yet to be discovered kernel issues the function is disabled by default and can be enabled with pci=use_crs. Signed-off-by: Gary Hade <gary.hade@us.ibm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/pci/acpi.c139
-rw-r--r--arch/x86/pci/common.c3
-rw-r--r--arch/x86/pci/pci.h1
3 files changed, 143 insertions, 0 deletions
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index 1dd6f3fc077d..c6fd3a6afa42 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -45,6 +45,142 @@ static struct dmi_system_id acpi_pciprobe_dmi_table[] = {
45 {} 45 {}
46}; 46};
47 47
48struct pci_root_info {
49 char *name;
50 unsigned int res_num;
51 struct resource *res;
52 struct pci_bus *bus;
53 int busnum;
54};
55
56static acpi_status
57resource_to_addr(struct acpi_resource *resource,
58 struct acpi_resource_address64 *addr)
59{
60 acpi_status status;
61
62 status = acpi_resource_to_address64(resource, addr);
63 if (ACPI_SUCCESS(status) &&
64 (addr->resource_type == ACPI_MEMORY_RANGE ||
65 addr->resource_type == ACPI_IO_RANGE) &&
66 addr->address_length > 0 &&
67 addr->producer_consumer == ACPI_PRODUCER) {
68 return AE_OK;
69 }
70 return AE_ERROR;
71}
72
73static acpi_status
74count_resource(struct acpi_resource *acpi_res, void *data)
75{
76 struct pci_root_info *info = data;
77 struct acpi_resource_address64 addr;
78 acpi_status status;
79
80 status = resource_to_addr(acpi_res, &addr);
81 if (ACPI_SUCCESS(status))
82 info->res_num++;
83 return AE_OK;
84}
85
86static acpi_status
87setup_resource(struct acpi_resource *acpi_res, void *data)
88{
89 struct pci_root_info *info = data;
90 struct resource *res;
91 struct acpi_resource_address64 addr;
92 acpi_status status;
93 unsigned long flags;
94 struct resource *root;
95
96 status = resource_to_addr(acpi_res, &addr);
97 if (!ACPI_SUCCESS(status))
98 return AE_OK;
99
100 if (addr.resource_type == ACPI_MEMORY_RANGE) {
101 root = &iomem_resource;
102 flags = IORESOURCE_MEM;
103 if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY)
104 flags |= IORESOURCE_PREFETCH;
105 } else if (addr.resource_type == ACPI_IO_RANGE) {
106 root = &ioport_resource;
107 flags = IORESOURCE_IO;
108 } else
109 return AE_OK;
110
111 res = &info->res[info->res_num];
112 res->name = info->name;
113 res->flags = flags;
114 res->start = addr.minimum + addr.translation_offset;
115 res->end = res->start + addr.address_length - 1;
116 res->child = NULL;
117
118 if (insert_resource(root, res)) {
119 printk(KERN_ERR "PCI: Failed to allocate 0x%lx-0x%lx "
120 "from %s for %s\n", (unsigned long) res->start,
121 (unsigned long) res->end, root->name, info->name);
122 } else {
123 info->bus->resource[info->res_num] = res;
124 info->res_num++;
125 }
126 return AE_OK;
127}
128
129static void
130adjust_transparent_bridge_resources(struct pci_bus *bus)
131{
132 struct pci_dev *dev;
133
134 list_for_each_entry(dev, &bus->devices, bus_list) {
135 int i;
136 u16 class = dev->class >> 8;
137
138 if (class == PCI_CLASS_BRIDGE_PCI && dev->transparent) {
139 for(i = 3; i < PCI_BUS_NUM_RESOURCES; i++)
140 dev->subordinate->resource[i] =
141 dev->bus->resource[i - 3];
142 }
143 }
144}
145
146static void
147get_current_resources(struct acpi_device *device, int busnum,
148 struct pci_bus *bus)
149{
150 struct pci_root_info info;
151 size_t size;
152
153 info.bus = bus;
154 info.res_num = 0;
155 acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_resource,
156 &info);
157 if (!info.res_num)
158 return;
159
160 size = sizeof(*info.res) * info.res_num;
161 info.res = kmalloc(size, GFP_KERNEL);
162 if (!info.res)
163 goto res_alloc_fail;
164
165 info.name = kmalloc(12, GFP_KERNEL);
166 if (!info.name)
167 goto name_alloc_fail;
168 sprintf(info.name, "PCI Bus #%02x", busnum);
169
170 info.res_num = 0;
171 acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource,
172 &info);
173 if (info.res_num)
174 adjust_transparent_bridge_resources(bus);
175
176 return;
177
178name_alloc_fail:
179 kfree(info.res);
180res_alloc_fail:
181 return;
182}
183
48struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int domain, int busnum) 184struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int domain, int busnum)
49{ 185{
50 struct pci_bus *bus; 186 struct pci_bus *bus;
@@ -89,6 +225,9 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int do
89 } 225 }
90 } 226 }
91#endif 227#endif
228
229 if (bus && (pci_probe & PCI_USE__CRS))
230 get_current_resources(device, busnum, bus);
92 231
93 return bus; 232 return bus;
94} 233}
diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c
index 7c4d75e3c5f3..7d6a9a5aa7cd 100644
--- a/arch/x86/pci/common.c
+++ b/arch/x86/pci/common.c
@@ -436,6 +436,9 @@ char * __devinit pcibios_setup(char *str)
436 } else if (!strcmp(str, "assign-busses")) { 436 } else if (!strcmp(str, "assign-busses")) {
437 pci_probe |= PCI_ASSIGN_ALL_BUSSES; 437 pci_probe |= PCI_ASSIGN_ALL_BUSSES;
438 return NULL; 438 return NULL;
439 } else if (!strcmp(str, "use_crs")) {
440 pci_probe |= PCI_USE__CRS;
441 return NULL;
439 } else if (!strcmp(str, "routeirq")) { 442 } else if (!strcmp(str, "routeirq")) {
440 pci_routeirq = 1; 443 pci_routeirq = 1;
441 return NULL; 444 return NULL;
diff --git a/arch/x86/pci/pci.h b/arch/x86/pci/pci.h
index 057f335fa3f6..ac56d3916c50 100644
--- a/arch/x86/pci/pci.h
+++ b/arch/x86/pci/pci.h
@@ -27,6 +27,7 @@
27#define PCI_BIOS_IRQ_SCAN 0x2000 27#define PCI_BIOS_IRQ_SCAN 0x2000
28#define PCI_ASSIGN_ALL_BUSSES 0x4000 28#define PCI_ASSIGN_ALL_BUSSES 0x4000
29#define PCI_CAN_SKIP_ISA_ALIGN 0x8000 29#define PCI_CAN_SKIP_ISA_ALIGN 0x8000
30#define PCI_USE__CRS 0x10000
30 31
31extern unsigned int pci_probe; 32extern unsigned int pci_probe;
32extern unsigned long pirq_table_addr; 33extern unsigned long pirq_table_addr;