The following patch attempts to fix an issue where multiple ACPI EC
events pile up during suspend and fill a buffer that upon resume
prevent further event notifications.

The fix clears up the event queue early on during resume and also upon
initial acpiec(4) attach.

Initially reported by Samsung users[1], this issue might affect a larger
range of models so we don't want to flush based on whitelisting.
We want to flush on all machines.

This forced flushing, although very unlikely, might provoke in theory
a bug where the BIOS might expect an event to be present upon resume.
Again this is a very unlikely thing to happen and doing it would require
serious BIOS contortions.

In order to clear this up and see if this helps some machines, I would
really appreciate if people would test the following diff on as many
models as possible and report back if anything changes in either a good
or bad way.

[1] - https://bugzilla.kernel.org/show_bug.cgi?id=44161


Index: acpiec.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpiec.c,v
retrieving revision 1.48
diff -u -p -r1.48 acpiec.c
--- acpiec.c    2 Jul 2013 18:37:47 -0000       1.48
+++ acpiec.c    26 Mar 2014 20:56:32 -0000
@@ -34,6 +34,7 @@
 
 int            acpiec_match(struct device *, void *, void *);
 void           acpiec_attach(struct device *, struct device *, void *);
+int            acpiec_activate(struct device *, int);
 
 u_int8_t       acpiec_status(struct acpiec_softc *);
 u_int8_t       acpiec_read_data(struct acpiec_softc *);
@@ -54,6 +55,7 @@ int           acpiec_getregister(const u_int8_t *
 
 void           acpiec_wait(struct acpiec_softc *, u_int8_t, u_int8_t);
 void           acpiec_sci_event(struct acpiec_softc *);
+void           acpiec_clear_events(struct acpiec_softc *);
 
 void           acpiec_get_events(struct acpiec_softc *);
 
@@ -82,7 +84,8 @@ void          acpiec_unlock(struct acpiec_softc 
 int    acpiec_reg(struct acpiec_softc *);
 
 struct cfattach acpiec_ca = {
-       sizeof(struct acpiec_softc), acpiec_match, acpiec_attach
+       sizeof(struct acpiec_softc), acpiec_match, acpiec_attach,
+       NULL, acpiec_activate
 };
 
 struct cfdriver acpiec_cd = {
@@ -296,6 +299,8 @@ acpiec_attach(struct device *parent, str
        acpi_set_gpehandler(sc->sc_acpi, sc->sc_gpe, acpiec_gpehandler,
            sc, 1);
 #endif
+
+       acpiec_clear_events(sc);
        
        if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_GLK", 0, NULL, &res))
                sc->sc_glk = 0;
@@ -307,6 +312,20 @@ acpiec_attach(struct device *parent, str
        printf("\n");
 }
 
+int
+acpiec_activate(struct device *self, int act)
+{
+       struct acpiec_softc *sc = (struct acpiec_softc *)self;
+
+
+       switch (act) {
+       case DVACT_RESUME:
+               acpiec_clear_events(sc);
+               break;
+       }
+       return (0);
+}
+
 void
 acpiec_get_events(struct acpiec_softc *sc)
 {
@@ -552,4 +571,18 @@ acpiec_unlock(struct acpiec_softc *sc)
        }
 
        sc->sc_ecbusy = 0;
+}
+
+void
+acpiec_clear_events(struct acpiec_softc *sc)
+{
+       int i;
+
+       for (i = 0; i < 100; i++) {
+               acpiec_write_cmd(sc, EC_CMD_QR);
+               sc->sc_gotsci = 0;
+               if ((acpiec_status(sc) & EC_STAT_SCI_EVT) != EC_STAT_SCI_EVT) {
+                       break;
+               }
+       }
 }

Reply via email to