On Sat, Sep 06, 2014 at 09:18:26PM +0200, Mark Kettenis wrote: > 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?
Just a nit, there's a dozen or so notify types in the spec, should we replace '2' with something more understandable like ACPI_PME_EVENT or similar? That suggestion should probably apply to the others we look at in other places like 0x81 / 0x82, etc. Maybe that's better left for another cleanup diff? Other than that, diff looks ok to me. -ml > > > 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 >