aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/pci/acpi.c
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/x86/pci/acpi.c
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/x86/pci/acpi.c')
-rw-r--r--arch/x86/pci/acpi.c139
1 files changed, 139 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}