As pea@ noticed, Apple machines with an NVIDIA MCP79 chipset will
suffer an interrupt storm when you send them WOL packet to their
nfe(4) interface.  The acpi0 interrupt handler fires continously in
this case.  Some digging revealed that the device wake GPE for the
nfe(4) interface was casuing this.  The machine's AML sends a
notification, suggesting that the device driver for the device needs
to take some action.  But ours doesn't.

Some more digging revealed that the PCIe PME status bit gets set.
Clearing that bit stops the interrupts and makes the machine happy
again.  

So here is a diff that installs a notification handler for all PCI
devices that clears the PME status bit if the device has one.  This
seems to be what Windows does.  At least I found some Microsoft
documentation that says that the PCI driver is responsible for
clearing the PME status bit if a PME event is received.

ok?


Index: acpi.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpi.c,v
retrieving revision 1.267
diff -u -p -r1.267 acpi.c
--- acpi.c      20 Jul 2014 18:05:21 -0000      1.267
+++ acpi.c      6 Sep 2014 18:54:38 -0000
@@ -74,6 +74,7 @@ int   acpi_hasprocfvs;
 void   acpi_pci_match(struct device *, struct pci_attach_args *);
 pcireg_t acpi_pci_min_powerstate(pci_chipset_tag_t, pcitag_t);
 void    acpi_pci_set_powerstate(pci_chipset_tag_t, pcitag_t, int, int);
+int    acpi_pci_notify(struct aml_node *, int, void *);
 
 int    acpi_match(struct device *, void *, void *);
 void   acpi_attach(struct device *, struct device *, void *);
@@ -567,6 +568,8 @@ acpi_pci_match(struct device *dev, struc
                state = pci_get_powerstate(pa->pa_pc, pa->pa_tag);
                acpi_pci_set_powerstate(pa->pa_pc, pa->pa_tag, state, 1);
                acpi_pci_set_powerstate(pa->pa_pc, pa->pa_tag, state, 0);
+
+               aml_register_notify(pdev->node, NULL, acpi_pci_notify, pdev, 0);
        }
 }
 
@@ -660,6 +663,29 @@ acpi_pci_set_powerstate(pci_chipset_tag_
 
        }
 #endif /* NACPIPWRRES > 0 */
+}
+
+int
+acpi_pci_notify(struct aml_node *node, int ntype, void *arg)
+{
+       struct acpi_pci *pdev = arg;
+       pci_chipset_tag_t pc = NULL;
+       pcitag_t tag;
+       pcireg_t reg;
+       int offset;
+
+       /* We're only interested in Device Wake notifications. */
+       if (ntype != 2)
+               return (0);
+
+       tag = pci_make_tag(pc, pdev->bus, pdev->dev, pdev->fun);
+       if (pci_get_capability(pc, tag, PCI_CAP_PWRMGMT, &offset, 0)) {
+               /* Clear the PME Status bit if it is set. */
+               reg = pci_conf_read(pc, tag, offset + PCI_PMCSR);
+               pci_conf_write(pc, tag, offset + PCI_PMCSR, reg);
+       }
+
+       return (0);
 }
 
 void

Reply via email to