diff mbox

[1/3] acpi: Provide default GPE handler if the firmware doesn't

Message ID 1257806687-6608-1-git-send-email-mjg@redhat.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Matthew Garrett Nov. 9, 2009, 10:44 p.m. UTC
None
diff mbox

Patch

diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index dae90cc..7fff59c 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -879,6 +879,124 @@  static struct acpi_bus_type acpi_pci_bus = {
 	.find_bridge = acpi_pci_find_root_bridge,
 };
 
+static void acpi_pci_bus_notify(struct work_struct *work)
+{
+	acpi_handle handle = acpi_get_pci_rootbridge_handle(0, 0);
+	struct acpi_device *acpi_dev;
+	struct acpi_pci_root *root;
+
+	kfree(work);
+
+	if (acpi_bus_get_device(handle, &acpi_dev))
+		return;
+
+	root = acpi_driver_data(acpi_dev);
+
+	if (!root)
+		return;
+
+	pci_pme_wakeup_bus(root->bus);
+}
+
+
+static void acpi_pci_uhci_notify(struct work_struct *work)
+{
+	struct pci_dev *pci_dev = NULL;
+
+	kfree(work);
+
+	while ((pci_dev = pci_get_class(PCI_CLASS_SERIAL_USB_UHCI, pci_dev))) {
+		pci_pme_wakeup(pci_dev);
+		pci_dev_put(pci_dev);
+	}
+}
+
+static void acpi_pci_audio_or_uhci_notify(struct work_struct *work)
+{
+	/* FIXME: Need to work out how to check what this actually maps to */
+	return acpi_pci_uhci_notify(work);
+}
+
+static acpi_status acpi_pci_pme_notify(void *context)
+{
+	struct work_struct *work = kzalloc(sizeof(struct work_struct),
+					   GFP_ATOMIC);
+
+	if (work) {
+		INIT_WORK(work, context);
+		schedule_work(work);
+	}
+	return AE_OK;
+}
+
+struct gpe_fixup {
+	int gpe;
+	work_func_t callback;
+};
+
+static struct gpe_fixup acpi_pci_intel_gpes[] = {
+	{ 0x3, &acpi_pci_uhci_notify, },
+	{ 0x4, &acpi_pci_uhci_notify, },
+	{ 0x5, &acpi_pci_audio_or_uhci_notify, },
+	{ 0xb, &acpi_pci_bus_notify, },
+	{ 0xc, &acpi_pci_uhci_notify, },
+	{ 0xd, &acpi_pci_bus_notify, },
+	{ 0xe, &acpi_pci_uhci_notify, },
+	{ 0x20, &acpi_pci_uhci_notify, },
+	{ 0, NULL, },
+};
+
+static void __init acpi_pci_install_gpe(u32 gpe_number,
+					work_func_t callback, int force)
+{
+	acpi_status status;
+	acpi_event_status event_status;
+
+	status = acpi_get_gpe_status(NULL, gpe_number, ACPI_NOT_ISR,
+				     &event_status);
+
+	/* Don't override existing methods by default */
+	if (!force && (event_status & ACPI_EVENT_FLAG_HANDLE))
+		return;
+
+	status = acpi_install_gpe_handler(NULL, gpe_number,
+					  ACPI_GPE_LEVEL_TRIGGERED,
+					  acpi_pci_pme_notify, callback);
+
+	if (ACPI_FAILURE(status) && status != AE_ALREADY_EXISTS) {
+		printk(KERN_INFO "Unable to install GPE fixup for %x\n",
+		       gpe_number);
+		acpi_remove_gpe_handler(NULL, gpe_number, acpi_pci_pme_notify);
+	}
+}
+
+static int __init acpi_pci_gpe_fixups(void)
+{
+	struct pci_dev *lpc;
+	struct gpe_fixup *fixups;
+	int i;
+
+	lpc = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, NULL);
+
+	if (!lpc)
+		return -ENODEV;
+
+	switch(lpc->vendor) {
+	case PCI_VENDOR_ID_INTEL:
+		fixups = acpi_pci_intel_gpes;
+		break;
+	}
+
+	if (fixups)
+		for (i=0; fixups[i].callback != NULL; i++)
+			acpi_pci_install_gpe(fixups[i].gpe,
+					     fixups[i].callback, false);
+
+	pci_dev_put(lpc);
+
+	return 0;
+}
+
 static int __init acpi_pci_init(void)
 {
 	int ret;
@@ -900,3 +1018,4 @@  static int __init acpi_pci_init(void)
 	return 0;
 }
 arch_initcall(acpi_pci_init);
+late_initcall(acpi_pci_gpe_fixups);