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
> 

Reply via email to