blob: fb9fdf4a42bf43acfd5d8a3c6c97bf3663adbbce [file] [log] [blame]
Bjorn Helgaas204d49a2009-10-26 11:20:47 -06001/*
2 * IOAPIC/IOxAPIC/IOSAPIC driver
3 *
4 * Copyright (C) 2009 Fujitsu Limited.
5 * (c) Copyright 2009 Hewlett-Packard Development Company, L.P.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12/*
13 * This driver manages PCI I/O APICs added by hotplug after boot. We try to
14 * claim all I/O APIC PCI devices, but those present at boot were registered
15 * when we parsed the ACPI MADT, so we'll fail when we try to re-register
16 * them.
17 */
18
19#include <linux/pci.h>
20#include <linux/acpi.h>
21#include <acpi/acpi_bus.h>
22
23struct ioapic {
24 acpi_handle handle;
25 u32 gsi_base;
26};
27
28static int ioapic_probe(struct pci_dev *dev, const struct pci_device_id *ent)
29{
30 acpi_handle handle;
31 acpi_status status;
32 unsigned long long gsb;
33 struct ioapic *ioapic;
Bjorn Helgaas204d49a2009-10-26 11:20:47 -060034 int ret;
35 char *type;
Bjorn Helgaase1944c62010-03-16 15:53:08 -060036 struct resource *res;
Bjorn Helgaas204d49a2009-10-26 11:20:47 -060037
38 handle = DEVICE_ACPI_HANDLE(&dev->dev);
39 if (!handle)
40 return -EINVAL;
41
42 status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsb);
43 if (ACPI_FAILURE(status))
44 return -EINVAL;
45
46 /*
47 * The previous code in acpiphp evaluated _MAT if _GSB failed, but
48 * ACPI spec 4.0 sec 6.2.2 requires _GSB for hot-pluggable I/O APICs.
49 */
50
51 ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL);
52 if (!ioapic)
53 return -ENOMEM;
54
55 ioapic->handle = handle;
56 ioapic->gsi_base = (u32) gsb;
57
58 if (dev->class == PCI_CLASS_SYSTEM_PIC_IOAPIC)
59 type = "IOAPIC";
60 else
61 type = "IOxAPIC";
62
63 ret = pci_enable_device(dev);
64 if (ret < 0)
65 goto exit_free;
66
67 pci_set_master(dev);
68
69 if (pci_request_region(dev, 0, type))
70 goto exit_disable;
71
Bjorn Helgaase1944c62010-03-16 15:53:08 -060072 res = &dev->resource[0];
73 if (acpi_register_ioapic(ioapic->handle, res->start, ioapic->gsi_base))
Bjorn Helgaas204d49a2009-10-26 11:20:47 -060074 goto exit_release;
75
76 pci_set_drvdata(dev, ioapic);
Bjorn Helgaase1944c62010-03-16 15:53:08 -060077 dev_info(&dev->dev, "%s at %pR, GSI %u\n", type, res, ioapic->gsi_base);
Bjorn Helgaas204d49a2009-10-26 11:20:47 -060078 return 0;
79
80exit_release:
81 pci_release_region(dev, 0);
82exit_disable:
83 pci_disable_device(dev);
84exit_free:
85 kfree(ioapic);
86 return -ENODEV;
87}
88
89static void ioapic_remove(struct pci_dev *dev)
90{
91 struct ioapic *ioapic = pci_get_drvdata(dev);
92
93 acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base);
94 pci_release_region(dev, 0);
95 pci_disable_device(dev);
96 kfree(ioapic);
97}
98
99
100static struct pci_device_id ioapic_devices[] = {
101 { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
102 PCI_CLASS_SYSTEM_PIC_IOAPIC << 8, 0xffff00, },
103 { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
104 PCI_CLASS_SYSTEM_PIC_IOXAPIC << 8, 0xffff00, },
105 { }
106};
107
108static struct pci_driver ioapic_driver = {
109 .name = "ioapic",
110 .id_table = ioapic_devices,
111 .probe = ioapic_probe,
112 .remove = __devexit_p(ioapic_remove),
113};
114
115static int __init ioapic_init(void)
116{
117 return pci_register_driver(&ioapic_driver);
118}
119
120static void __exit ioapic_exit(void)
121{
122 pci_unregister_driver(&ioapic_driver);
123}
124
125module_init(ioapic_init);
126module_exit(ioapic_exit);