@@ -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);