]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | #include <linux/pci.h> |
2 | #include <linux/acpi.h> | |
3 | #include <linux/init.h> | |
b33fa1f3 | 4 | #include <linux/irq.h> |
036fff4c | 5 | #include <linux/dmi.h> |
5a0e3ad6 | 6 | #include <linux/slab.h> |
69e1a33f | 7 | #include <asm/numa.h> |
82487711 | 8 | #include <asm/pci_x86.h> |
1da177e4 | 9 | |
62f420f8 | 10 | struct pci_root_info { |
42887b29 | 11 | struct acpi_device *bridge; |
fe05725f | 12 | char name[16]; |
35cb05e5 | 13 | struct pci_sysdata sd; |
c0fa4078 JL |
14 | #ifdef CONFIG_PCI_MMCONFIG |
15 | bool mcfg_added; | |
16 | u16 segment; | |
17 | u8 start_bus; | |
18 | u8 end_bus; | |
19 | #endif | |
62f420f8 GH |
20 | }; |
21 | ||
7bc5e3f2 | 22 | static bool pci_use_crs = true; |
1f09b09b | 23 | static bool pci_ignore_seg = false; |
7bc5e3f2 BH |
24 | |
25 | static int __init set_use_crs(const struct dmi_system_id *id) | |
26 | { | |
27 | pci_use_crs = true; | |
28 | return 0; | |
29 | } | |
30 | ||
28c3c05d DJ |
31 | static int __init set_nouse_crs(const struct dmi_system_id *id) |
32 | { | |
33 | pci_use_crs = false; | |
34 | return 0; | |
35 | } | |
36 | ||
1f09b09b BH |
37 | static int __init set_ignore_seg(const struct dmi_system_id *id) |
38 | { | |
39 | printk(KERN_INFO "PCI: %s detected: ignoring ACPI _SEG\n", id->ident); | |
40 | pci_ignore_seg = true; | |
41 | return 0; | |
42 | } | |
43 | ||
44 | static const struct dmi_system_id pci_crs_quirks[] __initconst = { | |
7bc5e3f2 BH |
45 | /* http://bugzilla.kernel.org/show_bug.cgi?id=14183 */ |
46 | { | |
47 | .callback = set_use_crs, | |
48 | .ident = "IBM System x3800", | |
49 | .matches = { | |
50 | DMI_MATCH(DMI_SYS_VENDOR, "IBM"), | |
51 | DMI_MATCH(DMI_PRODUCT_NAME, "x3800"), | |
52 | }, | |
53 | }, | |
2491762c BH |
54 | /* https://bugzilla.kernel.org/show_bug.cgi?id=16007 */ |
55 | /* 2006 AMD HT/VIA system with two host bridges */ | |
56 | { | |
57 | .callback = set_use_crs, | |
58 | .ident = "ASRock ALiveSATA2-GLAN", | |
59 | .matches = { | |
60 | DMI_MATCH(DMI_PRODUCT_NAME, "ALiveSATA2-GLAN"), | |
61 | }, | |
62 | }, | |
29cf7a30 PM |
63 | /* https://bugzilla.kernel.org/show_bug.cgi?id=30552 */ |
64 | /* 2006 AMD HT/VIA system with two host bridges */ | |
65 | { | |
66 | .callback = set_use_crs, | |
67 | .ident = "ASUS M2V-MX SE", | |
68 | .matches = { | |
69 | DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), | |
70 | DMI_MATCH(DMI_BOARD_NAME, "M2V-MX SE"), | |
71 | DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), | |
72 | }, | |
73 | }, | |
84113717 JN |
74 | /* https://bugzilla.kernel.org/show_bug.cgi?id=42619 */ |
75 | { | |
76 | .callback = set_use_crs, | |
77 | .ident = "MSI MS-7253", | |
78 | .matches = { | |
79 | DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), | |
80 | DMI_MATCH(DMI_BOARD_NAME, "MS-7253"), | |
81 | DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies, LTD"), | |
84113717 JN |
82 | }, |
83 | }, | |
1dace011 BH |
84 | /* https://bugs.launchpad.net/ubuntu/+source/alsa-driver/+bug/931368 */ |
85 | /* https://bugs.launchpad.net/ubuntu/+source/alsa-driver/+bug/1033299 */ | |
86 | { | |
87 | .callback = set_use_crs, | |
88 | .ident = "Foxconn K8M890-8237A", | |
89 | .matches = { | |
90 | DMI_MATCH(DMI_BOARD_VENDOR, "Foxconn"), | |
91 | DMI_MATCH(DMI_BOARD_NAME, "K8M890-8237A"), | |
92 | DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies, LTD"), | |
93 | }, | |
94 | }, | |
28c3c05d | 95 | |
e702781f DJ |
96 | /* Now for the blacklist.. */ |
97 | ||
98 | /* https://bugzilla.redhat.com/show_bug.cgi?id=769657 */ | |
99 | { | |
100 | .callback = set_nouse_crs, | |
101 | .ident = "Dell Studio 1557", | |
102 | .matches = { | |
103 | DMI_MATCH(DMI_BOARD_VENDOR, "Dell Inc."), | |
104 | DMI_MATCH(DMI_PRODUCT_NAME, "Studio 1557"), | |
105 | DMI_MATCH(DMI_BIOS_VERSION, "A09"), | |
106 | }, | |
107 | }, | |
8b6a5af9 DJ |
108 | /* https://bugzilla.redhat.com/show_bug.cgi?id=769657 */ |
109 | { | |
110 | .callback = set_nouse_crs, | |
111 | .ident = "Thinkpad SL510", | |
112 | .matches = { | |
113 | DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), | |
114 | DMI_MATCH(DMI_BOARD_NAME, "2847DFG"), | |
115 | DMI_MATCH(DMI_BIOS_VERSION, "6JET85WW (1.43 )"), | |
116 | }, | |
117 | }, | |
1f09b09b BH |
118 | |
119 | /* https://bugzilla.kernel.org/show_bug.cgi?id=15362 */ | |
120 | { | |
121 | .callback = set_ignore_seg, | |
122 | .ident = "HP xw9300", | |
123 | .matches = { | |
124 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | |
125 | DMI_MATCH(DMI_PRODUCT_NAME, "HP xw9300 Workstation"), | |
126 | }, | |
127 | }, | |
7bc5e3f2 BH |
128 | {} |
129 | }; | |
130 | ||
131 | void __init pci_acpi_crs_quirks(void) | |
132 | { | |
133 | int year; | |
134 | ||
3d9fecf6 BH |
135 | if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && year < 2008) { |
136 | if (iomem_resource.end <= 0xffffffff) | |
137 | pci_use_crs = false; | |
138 | } | |
7bc5e3f2 | 139 | |
1f09b09b | 140 | dmi_check_system(pci_crs_quirks); |
7bc5e3f2 BH |
141 | |
142 | /* | |
143 | * If the user specifies "pci=use_crs" or "pci=nocrs" explicitly, that | |
144 | * takes precedence over anything we figured out above. | |
145 | */ | |
146 | if (pci_probe & PCI_ROOT_NO_CRS) | |
147 | pci_use_crs = false; | |
148 | else if (pci_probe & PCI_USE__CRS) | |
149 | pci_use_crs = true; | |
150 | ||
151 | printk(KERN_INFO "PCI: %s host bridge windows from ACPI; " | |
152 | "if necessary, use \"pci=%s\" and report a bug\n", | |
153 | pci_use_crs ? "Using" : "Ignoring", | |
154 | pci_use_crs ? "nocrs" : "use_crs"); | |
155 | } | |
156 | ||
c0fa4078 | 157 | #ifdef CONFIG_PCI_MMCONFIG |
a18e3690 | 158 | static int check_segment(u16 seg, struct device *dev, char *estr) |
c0fa4078 JL |
159 | { |
160 | if (seg) { | |
161 | dev_err(dev, | |
162 | "%s can't access PCI configuration " | |
163 | "space under this host bridge.\n", | |
164 | estr); | |
165 | return -EIO; | |
166 | } | |
167 | ||
168 | /* | |
169 | * Failure in adding MMCFG information is not fatal, | |
170 | * just can't access extended configuration space of | |
171 | * devices under this host bridge. | |
172 | */ | |
173 | dev_warn(dev, | |
174 | "%s can't access extended PCI configuration " | |
175 | "space under this bridge.\n", | |
176 | estr); | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
a18e3690 GKH |
181 | static int setup_mcfg_map(struct pci_root_info *info, u16 seg, u8 start, |
182 | u8 end, phys_addr_t addr) | |
c0fa4078 JL |
183 | { |
184 | int result; | |
185 | struct device *dev = &info->bridge->dev; | |
186 | ||
187 | info->start_bus = start; | |
188 | info->end_bus = end; | |
189 | info->mcfg_added = false; | |
190 | ||
191 | /* return success if MMCFG is not in use */ | |
192 | if (raw_pci_ext_ops && raw_pci_ext_ops != &pci_mmcfg) | |
193 | return 0; | |
194 | ||
195 | if (!(pci_probe & PCI_PROBE_MMCONF)) | |
196 | return check_segment(seg, dev, "MMCONFIG is disabled,"); | |
197 | ||
198 | result = pci_mmconfig_insert(dev, seg, start, end, addr); | |
199 | if (result == 0) { | |
200 | /* enable MMCFG if it hasn't been enabled yet */ | |
201 | if (raw_pci_ext_ops == NULL) | |
202 | raw_pci_ext_ops = &pci_mmcfg; | |
203 | info->mcfg_added = true; | |
204 | } else if (result != -EEXIST) | |
205 | return check_segment(seg, dev, | |
206 | "fail to add MMCONFIG information,"); | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | static void teardown_mcfg_map(struct pci_root_info *info) | |
212 | { | |
213 | if (info->mcfg_added) { | |
214 | pci_mmconfig_delete(info->segment, info->start_bus, | |
215 | info->end_bus); | |
216 | info->mcfg_added = false; | |
217 | } | |
218 | } | |
219 | #else | |
a18e3690 | 220 | static int setup_mcfg_map(struct pci_root_info *info, |
c0fa4078 JL |
221 | u16 seg, u8 start, u8 end, |
222 | phys_addr_t addr) | |
223 | { | |
224 | return 0; | |
225 | } | |
226 | static void teardown_mcfg_map(struct pci_root_info *info) | |
227 | { | |
228 | } | |
229 | #endif | |
230 | ||
593669c2 JL |
231 | static void validate_resources(struct device *dev, struct list_head *crs_res, |
232 | unsigned long type) | |
62f420f8 | 233 | { |
593669c2 JL |
234 | LIST_HEAD(list); |
235 | struct resource *res1, *res2, *root = NULL; | |
236 | struct resource_entry *tmp, *entry, *entry2; | |
f9cde5ff | 237 | |
593669c2 JL |
238 | BUG_ON((type & (IORESOURCE_MEM | IORESOURCE_IO)) == 0); |
239 | root = (type & IORESOURCE_MEM) ? &iomem_resource : &ioport_resource; | |
2cdb3f1d | 240 | |
593669c2 JL |
241 | list_splice_init(crs_res, &list); |
242 | resource_list_for_each_entry_safe(entry, tmp, &list) { | |
243 | bool free = false; | |
244 | resource_size_t end; | |
4723d0f2 | 245 | |
593669c2 | 246 | res1 = entry->res; |
4723d0f2 | 247 | if (!(res1->flags & type)) |
593669c2 JL |
248 | goto next; |
249 | ||
250 | /* Exclude non-addressable range or non-addressable portion */ | |
251 | end = min(res1->end, root->end); | |
252 | if (end <= res1->start) { | |
253 | dev_info(dev, "host bridge window %pR (ignored, not CPU addressable)\n", | |
254 | res1); | |
255 | free = true; | |
256 | goto next; | |
257 | } else if (res1->end != end) { | |
258 | dev_info(dev, "host bridge window %pR ([%#llx-%#llx] ignored, not CPU addressable)\n", | |
259 | res1, (unsigned long long)end + 1, | |
260 | (unsigned long long)res1->end); | |
261 | res1->end = end; | |
262 | } | |
4723d0f2 | 263 | |
593669c2 JL |
264 | resource_list_for_each_entry(entry2, crs_res) { |
265 | res2 = entry2->res; | |
4723d0f2 BH |
266 | if (!(res2->flags & type)) |
267 | continue; | |
268 | ||
269 | /* | |
270 | * I don't like throwing away windows because then | |
271 | * our resources no longer match the ACPI _CRS, but | |
272 | * the kernel resource tree doesn't allow overlaps. | |
273 | */ | |
74d24b21 | 274 | if (resource_overlaps(res1, res2)) { |
3ad674d6 AN |
275 | res2->start = min(res1->start, res2->start); |
276 | res2->end = max(res1->end, res2->end); | |
593669c2 | 277 | dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n", |
3ad674d6 | 278 | res2, res1); |
593669c2 JL |
279 | free = true; |
280 | goto next; | |
4723d0f2 BH |
281 | } |
282 | } | |
593669c2 JL |
283 | |
284 | next: | |
285 | resource_list_del(entry); | |
286 | if (free) | |
287 | resource_list_free_entry(entry); | |
288 | else | |
289 | resource_list_add_tail(entry, crs_res); | |
4723d0f2 BH |
290 | } |
291 | } | |
292 | ||
9a03d28d | 293 | static void add_resources(struct pci_root_info *info, |
593669c2 JL |
294 | struct list_head *resources, |
295 | struct list_head *crs_res) | |
4723d0f2 | 296 | { |
593669c2 JL |
297 | struct resource_entry *entry, *tmp; |
298 | struct resource *res, *conflict, *root = NULL; | |
4723d0f2 | 299 | |
593669c2 JL |
300 | validate_resources(&info->bridge->dev, crs_res, IORESOURCE_MEM); |
301 | validate_resources(&info->bridge->dev, crs_res, IORESOURCE_IO); | |
4723d0f2 | 302 | |
593669c2 JL |
303 | resource_list_for_each_entry_safe(entry, tmp, crs_res) { |
304 | res = entry->res; | |
4723d0f2 BH |
305 | if (res->flags & IORESOURCE_MEM) |
306 | root = &iomem_resource; | |
307 | else if (res->flags & IORESOURCE_IO) | |
308 | root = &ioport_resource; | |
42887b29 | 309 | else |
593669c2 | 310 | BUG_ON(res); |
4723d0f2 BH |
311 | |
312 | conflict = insert_resource_conflict(root, res); | |
593669c2 | 313 | if (conflict) { |
43d786ed BH |
314 | dev_info(&info->bridge->dev, |
315 | "ignoring host bridge window %pR (conflicts with %s %pR)\n", | |
316 | res, conflict->name, conflict); | |
593669c2 JL |
317 | resource_list_destroy_entry(entry); |
318 | } | |
62f420f8 | 319 | } |
62f420f8 | 320 | |
593669c2 | 321 | list_splice_tail(crs_res, resources); |
fd3b0c1e YL |
322 | } |
323 | ||
593669c2 | 324 | static void release_pci_root_info(struct pci_host_bridge *bridge) |
fd3b0c1e | 325 | { |
fd3b0c1e | 326 | struct resource *res; |
593669c2 JL |
327 | struct resource_entry *entry; |
328 | struct pci_root_info *info = bridge->release_data; | |
fd3b0c1e | 329 | |
593669c2 JL |
330 | resource_list_for_each_entry(entry, &bridge->windows) { |
331 | res = entry->res; | |
332 | if (res->parent && | |
333 | (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) | |
334 | release_resource(res); | |
fd3b0c1e YL |
335 | } |
336 | ||
c0fa4078 | 337 | teardown_mcfg_map(info); |
fd3b0c1e YL |
338 | kfree(info); |
339 | } | |
c0fa4078 | 340 | |
2c62e849 JL |
341 | /* |
342 | * An IO port or MMIO resource assigned to a PCI host bridge may be | |
343 | * consumed by the host bridge itself or available to its child | |
344 | * bus/devices. The ACPI specification defines a bit (Producer/Consumer) | |
345 | * to tell whether the resource is consumed by the host bridge itself, | |
346 | * but firmware hasn't used that bit consistently, so we can't rely on it. | |
347 | * | |
348 | * On x86 and IA64 platforms, all IO port and MMIO resources are assumed | |
349 | * to be available to child bus/devices except one special case: | |
350 | * IO port [0xCF8-0xCFF] is consumed by the host bridge itself | |
351 | * to access PCI configuration space. | |
352 | * | |
353 | * So explicitly filter out PCI CFG IO ports[0xCF8-0xCFF]. | |
354 | */ | |
355 | static bool resource_is_pcicfg_ioport(struct resource *res) | |
356 | { | |
357 | return (res->flags & IORESOURCE_IO) && | |
358 | res->start == 0xCF8 && res->end == 0xCFF; | |
359 | } | |
360 | ||
da5d727c BH |
361 | static void probe_pci_root_info(struct pci_root_info *info, |
362 | struct acpi_device *device, | |
593669c2 JL |
363 | int busnum, int domain, |
364 | struct list_head *list) | |
62f420f8 | 365 | { |
593669c2 | 366 | int ret; |
63f1789e | 367 | struct resource_entry *entry, *tmp; |
62f420f8 | 368 | |
5c1d81d1 | 369 | sprintf(info->name, "PCI Bus %04x:%02x", domain, busnum); |
baa495d9 | 370 | info->bridge = device; |
593669c2 JL |
371 | ret = acpi_dev_get_resources(device, list, |
372 | acpi_dev_filter_resource_type_cb, | |
373 | (void *)(IORESOURCE_IO | IORESOURCE_MEM)); | |
374 | if (ret < 0) | |
375 | dev_warn(&device->dev, | |
376 | "failed to parse _CRS method, error code %d\n", ret); | |
377 | else if (ret == 0) | |
378 | dev_dbg(&device->dev, | |
379 | "no IO and memory resources present in _CRS\n"); | |
380 | else | |
63f1789e | 381 | resource_list_for_each_entry_safe(entry, tmp, list) { |
2c62e849 JL |
382 | if ((entry->res->flags & IORESOURCE_DISABLED) || |
383 | resource_is_pcicfg_ioport(entry->res)) | |
63f1789e JL |
384 | resource_list_destroy_entry(entry); |
385 | else | |
386 | entry->res->name = info->name; | |
387 | } | |
62f420f8 GH |
388 | } |
389 | ||
a18e3690 | 390 | struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) |
1da177e4 | 391 | { |
57283776 | 392 | struct acpi_device *device = root->device; |
8928d5a6 | 393 | struct pci_root_info *info; |
57283776 BH |
394 | int domain = root->segment; |
395 | int busnum = root->secondary.start; | |
593669c2 JL |
396 | struct resource_entry *res_entry; |
397 | LIST_HEAD(crs_res); | |
2cd6975a | 398 | LIST_HEAD(resources); |
8928d5a6 | 399 | struct pci_bus *bus; |
08f1c192 | 400 | struct pci_sysdata *sd; |
871d5f8d | 401 | int node; |
08f1c192 | 402 | |
1f09b09b BH |
403 | if (pci_ignore_seg) |
404 | domain = 0; | |
405 | ||
a79e4198 | 406 | if (domain && !pci_domains_supported) { |
2a6bed83 BH |
407 | printk(KERN_WARNING "pci_bus %04x:%02x: " |
408 | "ignored (multiple domains not supported)\n", | |
409 | domain, busnum); | |
a79e4198 JG |
410 | return NULL; |
411 | } | |
412 | ||
ab6ffce3 | 413 | node = acpi_get_node(device->handle); |
33673101 | 414 | if (node == NUMA_NO_NODE) { |
6616dbdf | 415 | node = x86_pci_root_bus_node(busnum); |
33673101 MS |
416 | if (node != 0 && node != NUMA_NO_NODE) |
417 | dev_info(&device->dev, FW_BUG "no _PXM; falling back to node %d from hardware (may be inconsistent with ACPI node numbers)\n", | |
418 | node); | |
419 | } | |
b755de8d | 420 | |
8a3d01c7 BH |
421 | if (node != NUMA_NO_NODE && !node_online(node)) |
422 | node = NUMA_NO_NODE; | |
871d5f8d | 423 | |
965cd0e4 | 424 | info = kzalloc_node(sizeof(*info), GFP_KERNEL, node); |
35cb05e5 | 425 | if (!info) { |
2a6bed83 BH |
426 | printk(KERN_WARNING "pci_bus %04x:%02x: " |
427 | "ignored (out of memory)\n", domain, busnum); | |
08f1c192 MBY |
428 | return NULL; |
429 | } | |
69e1a33f | 430 | |
35cb05e5 | 431 | sd = &info->sd; |
a79e4198 | 432 | sd->domain = domain; |
871d5f8d | 433 | sd->node = node; |
7b199811 | 434 | sd->companion = device; |
affbda86 | 435 | |
b87e81e5 | 436 | bus = pci_find_bus(domain, busnum); |
437 | if (bus) { | |
438 | /* | |
affbda86 BH |
439 | * If the desired bus has been scanned already, replace |
440 | * its bus->sysdata. | |
b87e81e5 | 441 | */ |
442 | memcpy(bus->sysdata, sd, sizeof(*sd)); | |
fd3b0c1e | 443 | kfree(info); |
626fdfec | 444 | } else { |
5c1d81d1 YL |
445 | /* insert busn res at first */ |
446 | pci_add_resource(&resources, &root->secondary); | |
593669c2 | 447 | |
316d86fe BH |
448 | /* |
449 | * _CRS with no apertures is normal, so only fall back to | |
450 | * defaults or native bridge info if we're ignoring _CRS. | |
451 | */ | |
593669c2 JL |
452 | probe_pci_root_info(info, device, busnum, domain, &crs_res); |
453 | if (pci_use_crs) { | |
454 | add_resources(info, &resources, &crs_res); | |
455 | } else { | |
456 | resource_list_for_each_entry(res_entry, &crs_res) | |
457 | dev_printk(KERN_DEBUG, &device->dev, | |
458 | "host bridge window %pR (ignored)\n", | |
459 | res_entry->res); | |
460 | resource_list_free(&crs_res); | |
2cd6975a | 461 | x86_pci_root_bus_resources(busnum, &resources); |
9a03d28d | 462 | } |
fd3b0c1e | 463 | |
c0fa4078 JL |
464 | if (!setup_mcfg_map(info, domain, (u8)root->secondary.start, |
465 | (u8)root->secondary.end, root->mcfg_addr)) | |
466 | bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, | |
467 | sd, &resources); | |
468 | ||
fd3b0c1e | 469 | if (bus) { |
5c1d81d1 | 470 | pci_scan_child_bus(bus); |
fd3b0c1e YL |
471 | pci_set_host_bridge_release( |
472 | to_pci_host_bridge(bus->bridge), | |
473 | release_pci_root_info, info); | |
474 | } else { | |
593669c2 JL |
475 | resource_list_free(&resources); |
476 | teardown_mcfg_map(info); | |
477 | kfree(info); | |
fd3b0c1e | 478 | } |
626fdfec | 479 | } |
08f1c192 | 480 | |
b03e7495 JM |
481 | /* After the PCI-E bus has been walked and all devices discovered, |
482 | * configure any settings of the fabric that might be necessary. | |
483 | */ | |
484 | if (bus) { | |
485 | struct pci_bus *child; | |
a58674ff BH |
486 | list_for_each_entry(child, &bus->children, node) |
487 | pcie_bus_configure_settings(child); | |
b03e7495 JM |
488 | } |
489 | ||
ab6ffce3 | 490 | if (bus && node != NUMA_NO_NODE) |
2b8c2efe | 491 | dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node); |
62f420f8 | 492 | |
69e1a33f | 493 | return bus; |
1da177e4 LT |
494 | } |
495 | ||
6c0cc950 RW |
496 | int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) |
497 | { | |
dc4fdaf0 RW |
498 | /* |
499 | * We pass NULL as parent to pci_create_root_bus(), so if it is not NULL | |
500 | * here, pci_create_root_bus() has been called by someone else and | |
501 | * sysdata is likely to be different from what we expect. Let it go in | |
502 | * that case. | |
503 | */ | |
504 | if (!bridge->dev.parent) { | |
505 | struct pci_sysdata *sd = bridge->bus->sysdata; | |
506 | ACPI_COMPANION_SET(&bridge->dev, sd->companion); | |
507 | } | |
6c0cc950 RW |
508 | return 0; |
509 | } | |
510 | ||
8dd779b1 | 511 | int __init pci_acpi_init(void) |
1da177e4 LT |
512 | { |
513 | struct pci_dev *dev = NULL; | |
514 | ||
1da177e4 | 515 | if (acpi_noirq) |
b72d0db9 | 516 | return -ENODEV; |
1da177e4 LT |
517 | |
518 | printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n"); | |
519 | acpi_irq_penalty_init(); | |
1da177e4 | 520 | pcibios_enable_irq = acpi_pci_irq_enable; |
87bec66b | 521 | pcibios_disable_irq = acpi_pci_irq_disable; |
ab3b3793 | 522 | x86_init.pci.init_irq = x86_init_noop; |
1da177e4 LT |
523 | |
524 | if (pci_routeirq) { | |
525 | /* | |
526 | * PCI IRQ routing is set up by pci_enable_device(), but we | |
527 | * also do it here in case there are still broken drivers that | |
528 | * don't use pci_enable_device(). | |
529 | */ | |
530 | printk(KERN_INFO "PCI: Routing PCI interrupts for all devices because \"pci=routeirq\" specified\n"); | |
fb37fb96 | 531 | for_each_pci_dev(dev) |
1da177e4 | 532 | acpi_pci_irq_enable(dev); |
657472e9 | 533 | } |
1da177e4 | 534 | |
1da177e4 LT |
535 | return 0; |
536 | } |