ioapic.c 2.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*
 * IOAPIC/IOxAPIC/IOSAPIC driver
 *
 * Copyright (C) 2009 Fujitsu Limited.
 * (c) Copyright 2009 Hewlett-Packard Development Company, L.P.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

/*
 * This driver manages PCI I/O APICs added by hotplug after boot.  We try to
 * claim all I/O APIC PCI devices, but those present at boot were registered
 * when we parsed the ACPI MADT, so we'll fail when we try to re-register
 * them.
 */

#include <linux/pci.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>

struct ioapic {
	acpi_handle	handle;
	u32		gsi_base;
};

static int ioapic_probe(struct pci_dev *dev, const struct pci_device_id *ent)
{
	acpi_handle handle;
	acpi_status status;
	unsigned long long gsb;
	struct ioapic *ioapic;
	int ret;
	char *type;
36
	struct resource *res;
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

	handle = DEVICE_ACPI_HANDLE(&dev->dev);
	if (!handle)
		return -EINVAL;

	status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsb);
	if (ACPI_FAILURE(status))
		return -EINVAL;

	/*
	 * The previous code in acpiphp evaluated _MAT if _GSB failed, but
	 * ACPI spec 4.0 sec 6.2.2 requires _GSB for hot-pluggable I/O APICs.
	 */

	ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL);
	if (!ioapic)
		return -ENOMEM;

	ioapic->handle = handle;
	ioapic->gsi_base = (u32) gsb;

	if (dev->class == PCI_CLASS_SYSTEM_PIC_IOAPIC)
		type = "IOAPIC";
	else
		type = "IOxAPIC";

	ret = pci_enable_device(dev);
	if (ret < 0)
		goto exit_free;

	pci_set_master(dev);

	if (pci_request_region(dev, 0, type))
		goto exit_disable;

72 73
	res = &dev->resource[0];
	if (acpi_register_ioapic(ioapic->handle, res->start, ioapic->gsi_base))
74 75 76
		goto exit_release;

	pci_set_drvdata(dev, ioapic);
77
	dev_info(&dev->dev, "%s at %pR, GSI %u\n", type, res, ioapic->gsi_base);
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
	return 0;

exit_release:
	pci_release_region(dev, 0);
exit_disable:
	pci_disable_device(dev);
exit_free:
	kfree(ioapic);
	return -ENODEV;
}

static void ioapic_remove(struct pci_dev *dev)
{
	struct ioapic *ioapic = pci_get_drvdata(dev);

	acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base);
	pci_release_region(dev, 0);
	pci_disable_device(dev);
	kfree(ioapic);
}


static struct pci_device_id ioapic_devices[] = {
	{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
	  PCI_CLASS_SYSTEM_PIC_IOAPIC << 8, 0xffff00, },
	{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
	  PCI_CLASS_SYSTEM_PIC_IOXAPIC << 8, 0xffff00, },
	{ }
};

static struct pci_driver ioapic_driver = {
	.name		= "ioapic",
	.id_table	= ioapic_devices,
	.probe		= ioapic_probe,
	.remove		= __devexit_p(ioapic_remove),
};

static int __init ioapic_init(void)
{
	return pci_register_driver(&ioapic_driver);
}

static void __exit ioapic_exit(void)
{
	pci_unregister_driver(&ioapic_driver);
}

module_init(ioapic_init);
module_exit(ioapic_exit);