From patchwork Mon Nov 9 22:44:45 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Garrett X-Patchwork-Id: 58870 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id nA9MjhdX005803 for ; Mon, 9 Nov 2009 22:45:43 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754238AbZKIWpK (ORCPT ); Mon, 9 Nov 2009 17:45:10 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1755082AbZKIWpH (ORCPT ); Mon, 9 Nov 2009 17:45:07 -0500 Received: from cavan.codon.org.uk ([93.93.128.6]:49367 "EHLO cavan.codon.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754238AbZKIWpF (ORCPT ); Mon, 9 Nov 2009 17:45:05 -0500 Received: from lan-nat-pool-bos.redhat.com ([66.187.234.200] helo=localhost.localdomain) by cavan.codon.org.uk with esmtpsa (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.69) (envelope-from ) id 1N7cys-00014C-EQ; Mon, 09 Nov 2009 22:45:02 +0000 From: Matthew Garrett To: linux-acpi@vger.kernel.org Cc: rjw@sisk.pl, robert.moore@intel.com, Matthew Garrett Subject: [PATCH 1/3] acpi: Provide default GPE handler if the firmware doesn't Date: Mon, 9 Nov 2009 17:44:45 -0500 Message-Id: <1257806687-6608-1-git-send-email-mjg@redhat.com> X-Mailer: git-send-email 1.6.5.2 X-SA-Do-Not-Run: Yes X-SA-Exim-Connect-IP: 66.187.234.200 X-SA-Exim-Mail-From: mjg@redhat.com X-SA-Exim-Scanned: No (on cavan.codon.org.uk); SAEximRunCond expanded to false Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org 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);