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