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; + } + } }
