FINALLY!! I found the exact problem _and_ a SOLUTION!!!   (NOTE: see
attached program)

The problem is that Samsung's embedded controller stops sending GPE
events if it still is waiting for previous GPE events to be queried from
it.

It will remain in that state until one hits the reset hole in the back
of the latop, or flashes a bios, or queries the events.

---> But how will the OS query the events if the GPE interrupt isn't
produced anymore? Classic chicken and egg. <----

So I made a small program that does just that -- it queries events from
the EC until there are no more to query. AND IT WORKED!! This restores
the normal behavior, and AC, Battery and LID events start to be produced
again. One would ideally run it after resume from sleep. I based it
after observing normal behavior in <linuxkernel>/drivers/acpi/ec.c, and
afterwards behaviour with laptop in "problem state".

Following is the program (I will attach it too). You can either:

     1) Run it by hand after resume from sleep.

     2) Or Automatically run it from /usr/lib/pm-utils/sleep.d/99-your-
script-here  (you can use 95led as basis).

     3) Or Insert this code in the linux kernel at the beginning of the
acpi_ec_unblock_transactions_early() function in
"<linuxkernel>/drivers/acpi/ec.c" and recompile the kernel. The cmd and
data ports are known in ec.c so they don't need to be hardcoded there.

PS: Other things that I already tried that didn't fix it:
    1) Please note that I already tried commenting "acpi_disable_all_gpes()" 
from "<linuxkernel>/drivers/acpi/sleep.c" and this didn't have an effect (I 
guess unreplied events were already accumulated in the EC prior to resume).
    2) I also tried reverting to calling acpi_enable() on sleep.c instead of 
setting the ACPI_BITREG_SCI_ENABLE. No effect, issue still resurfaced after 
some suspends.
    3) I also tried commenting out the call to 
acpi_ec_unblock_transactions_early() in sleep.c, but no effect.

PS2: Another way to force the embedded controller to be in a problem state that 
I discovered, is to:
      1)   echo disable > /sys/firmware/acpi/interrupts/gpe17
      2)   plug, unplug, plug, unplug, plug, unplug, plug, unplug  (8 actions)
      3)   echo enable > /sys/firmware/acpi/interrupts/gpe17


Following are the two files with fixes which I'll attach too:

NOTE: you must check that embedded controller ports are 0x62 and 0x66
looking in your DSDT. RUN AT YOUR OWN RISK!!!

---- samsung_fix_ec_events.c ------------------------------------------
#include <stdio.h>
#include <unistd.h>
#include <sys/io.h>

/* Compile with:
 *    gcc -o samsung_fix_ec_events samsung_fix_ec_events.c
 * Run as:
 *    sudo ./samsung_fix_ec_events
 * You may copy it to /usr/local/bin and then call it 
 * automatically after resume from sleep with a /usr/lib/pm-utils/sleep.d 
script:
 *    sudo cp ./samsung_fix_ec_events /usr/local/bin/
 */

/* Constants: */
enum ec_command {
    /* Standard command for querying LID, Battery and AC events: */
    ACPI_EC_COMMAND_QUERY = 0x84,

    /* ATTENTION: PLEASE edit the following two according to your DSDT. 
     * For "Samsung Series 5 NP530U3C", they are 0x62 and 0x66 respectively.
     *   They seem to be the same for many other samsung models, but if you 
don't check 
     * them first in the H_EC._CRS section of your DSDT, you run it at YOUR OWN 
RISK: */
    EC_DATA_PORT = 0x62,  
    EC_COMMAND_PORT = 0x66
};

int main (int argc, char** argv) {
    char status = 0;
    char data = 0;
    int count = 0;

    if (iopl(3)) {
        printf("Error: Permission to read/write to EmbeddedController port not 
granted.\n");
        return 1;
    }

    /* Query AC, Battery, LID, etc. events until there are no more. 
     * This clears them for the EC so that it can send them again in the 
future, thus unblocking the EC. */
    do {
        outb(ACPI_EC_COMMAND_QUERY, EC_COMMAND_PORT);
        status = inb(EC_COMMAND_PORT);
        data = inb(EC_DATA_PORT);
        /* printf("CommandQuery 0x84, status=0x%x, data=0x%x\n", status, data); 
*/
    } while (data != 0 && ++count < 10000);  /* Note: No less than a thousand 
max count */

    printf("EmbeddedController GPE events flushed. New events can be
produced now.\n");

    return 0;
}
----END samsung_fix_ec_events.c ------------------------------------------


----- 99samsung_fix_ec_events ----------------------------------------------
#!/bin/sh
# NOTE: Put this file in:  /usr/lib/pm-utils/sleep.d/99samsung_fix_ec_events
#
# On some samsung laptops (series 5 2012, series 9 2011, etc) , if many EC
# GPE events are produced during sleep (AC  plugged/unplugged, battery % change
# change, LID open/close, etc), and they are not queried, the
# EmbeddedController stops sending them and this creates a chicken and egg
# situation, that can only be resolved either by hitting the reset button in
# the back while powered off, or by simply forcing a query for the events here 
after resume.

case "$1" in
        hibernate|suspend)
                ;;
        thaw|resume) 
                #NOTE: edit this path if necessary to point to the program with 
the fix:
                /usr/local/bin/samsung_fix_ec_events
                ;;
        *) exit $NA
                ;;
esac

exit 0

----- END 99samsung_fix_ec_events
-------------------------------------------

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/971061

Title:
  acpi reports battery state incorrectly

To manage notifications about this bug go to:
https://bugs.launchpad.net/acpi/+bug/971061/+subscriptions

-- 
ubuntu-bugs mailing list
ubuntu-bugs@lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to