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
>