LINUX.ORG.RU

ideapad. Нашёл способ управления зарядом батареи.


10

5

Привет, ребята.

У меня есть ноутбук Lenovo ideapad G580 (20150). И у этого ноутбука под венду есть утилита под названием «lenovo energy management». Среди возможностей этой утилиты есть и функция управления зарядом батареи - полный заряд и режим защиты аккумулятора. В Linux этой фичей пользоваться нельзя, нет средств для этого (tp_smapi и tpacpi-bat только для настоящих ThinkPad'ов). Меня этот факт очень огорчал и я провел изыскания в результате которых нашел способ переключения режимов батареи.

Вообще управляется эта фигня через Super_I/O, но не суть важно.

Для начала нам нужен оффтопик с установленным «lenovo energy management» и тулзой RWEverything.

Запускаем RWEverything и тыкаем иконку EC скрин. Переключаем в «lenovo energy management» режимы батареи и смотрим какой байт меняется в окошке «Embedded Controller». В моем случае это байт по адресу «0A», 21 - защита батареи, 41 - полный заряд. А EC_SC/EC_DATA это адреса регистров, которые нам потом понадобятся. Все ясно, перезагружаемся в линукс.

Сначала я хотел использовать superiotool, но фиг там:

# superiotool 
superiotool r6637
No Super I/O found

Однако удача была со мной, и я нашел fanctrl.c, который и послужил основой для моей поделки.

Собственно моя поделка:

#include <stdint.h>
#include <sys/io.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* Поменяйте на свои значения */
#define EC_SC 0x66
#define EC_DATA 0x62
#define IBF 1
#define OBF 0
#define EC_SC_READ_CMD 0x80
#define EC_SC_WRITE_CMD 0x81
#define EC_SC_SCI_CMD 0x84
#define BATT_PORT 0x0a
#define BATT_LIMIT 0x21
#define BATT_FULL 0x41

static void init()
{
    if (ioperm(EC_DATA, 1, 1) != 0)
    {
        perror("ioperm(EC_DATA, 1, 1)");
        exit(1);
    }

    if (ioperm(EC_SC, 1, 1) != 0)
    {
        perror("ioperm(EC_SC, 1, 1)");
        exit(1);
    }
}

static void wait_ec(const uint32_t port, const uint32_t flag, const char value)
{
    uint8_t data;
    int i;

    i = 0;
    data = inb(port);

    while ( (((data >> flag) & 0x1) != value) && (i++ < 100) )
    {
        usleep(1000);
        data = inb(port);
    }

    if (i >= 100)
    {
        fprintf(stderr, "wait_ec error on port 0x%x, data=0x%x, flag=0x%x, value=0x%x\n", port, data, flag, value);
        exit(1);
    }
}

static uint8_t read_ec(const uint32_t port)
{
    uint8_t value;

    wait_ec(EC_SC, IBF, 0);
    outb(EC_SC_READ_CMD, EC_SC);
    wait_ec(EC_SC, IBF, 0);
    outb(port, EC_DATA);
    //wait_ec(EC_SC, EC_SC_IBF_FREE);
    wait_ec(EC_SC, OBF, 1);
    value = inb(EC_DATA);

    return value;
}

static void write_ec(const uint32_t port, const uint8_t value)
{
    wait_ec(EC_SC, IBF, 0);
    outb(EC_SC_WRITE_CMD, EC_SC);
    wait_ec(EC_SC, IBF, 0);
    outb(port, EC_DATA);
    wait_ec(EC_SC, IBF, 0);
    outb(value, EC_DATA);
    wait_ec(EC_SC, IBF, 0);
}

static void dump_all_regs(void)
{
    uint8_t val;
    int i;

    printf("EC reg dump:");

    for (i = 0x00; i <= 0xff; i++)
    {
        if ((i % 16) == 0)
        {
            printf("\n 0x%02x: ", i);
        }

        val = read_ec(i);
        printf("%02x ", val);
    }

    printf("\n");
}

static void set_value(const uint8_t value)
{
    uint8_t rval;

    rval = read_ec(BATT_PORT);
    printf("old value %02x\n", rval);
    write_ec(BATT_PORT, value);
    rval = read_ec(BATT_PORT);
    printf("new value %02x\n", rval);
}

int main(int argc, char *argv[])
{
    init();

    if (argc < 2)
    {
        dump_all_regs();
    }
    else
    {
        if (argv[1][0] == 'f')
        {
            printf("set full charge\n");
            set_value(BATT_FULL);
        }
        else if (argv[1][0] == 'l')
        {
            printf("set limited charge\n");
            set_value(BATT_LIMIT);
        }
        else
        {
            printf("unknown option\n");
        }
    }

    return 0;
}

А это её работа:

# acpi
Battery 0: Unknown, 60%
# ./a.out f
set full charge
old value 21
new value 41
# acpi
Battery 0: Charging, 61%, 00:01:23 until charged
# ./a.out l
set limited charge
old value 41
new value 21
# acpi
Battery 0: Discharging, 61%, 01:52:05 remaining
# acpi
Battery 0: Discharging, 60%, 01:49:36 remaining
# acpi
Battery 0: Discharging, 60%, 01:47:34 remaining
# acpi
Battery 0: Unknown, 60%


Хотелось бы воспользоваться, но не имею оффтопика. У меня U410. Подкиньте адреса регистров EC для него!

CYB3R ★★★★★ ()
Ответ на: комментарий от CYB3R

Попробуй запусти прожку с моими регистрами и без аргументов. Она выведет все значения. Посмотри что будет в 11ом байте (21 или 41).

PoMbl4 ()
Ответ на: комментарий от PoMbl4

Ничего похожего.

EC reg dump:
 0x00: 0a 80 05 00 00 00 00 00 00 00 7a 7a 7a 7a 7a 7a 
 0x10: 7a 7a 00 00 00 00 00 f2 00 c0 16 35 42 01 01 01 
 0x20: 00 00 00 00 c9 0f d3 0f 02 09 00 00 00 00 00 00 
 0x30: 00 00 00 00 00 00 00 00 00 00 00 00 0a 14 f1 00 
 0x40: eb 43 24 74 21 29 1e 1b 00 02 00 00 00 00 00 00 
 0x50: 00 00 00 02 5d 00 01 00 2b 00 00 00 03 01 45 00 
 0x60: 00 00 08 00 00 00 64 07 01 01 f1 00 14 50 05 0c 
 0x70: 03 03 10 40 08 e0 00 5f 10 c2 00 00 c0 8d 22 00 
 0x80: 00 00 00 00 00 00 00 ff 00 c3 00 00 f0 0d 00 00 
 0x90: 80 01 cc cc ff ff 20 cd 00 00 00 a9 00 00 00 00 
 0xa0: 53 4d 50 00 31 31 00 00 00 00 00 00 00 00 00 00 
 0xb0: 00 00 00 00 00 00 00 11 15 11 00 00 00 00 03 bd 
 0xc0: c2 00 00 f7 14 9b 1f 00 00 00 00 00 00 00 00 00 
 0xd0: 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 
 0xe0: f7 15 00 00 00 00 00 00 00 00 00 00 00 20 00 00 
 0xf0: 47 00 00 01 00 00 01 02 00 0d 00 00 00 00 01 00 

CYB3R ★★★★★ ()
Ответ на: комментарий от PoMbl4

Так?

#define EC_SC 0x66
#define EC_DATA 0x62
#define IBF 1
#define OBF 0
#define EC_SC_READ_CMD 0x97 // 151 dec
#define EC_SC_WRITE_CMD 0x93 // 147 dec
#define EC_SC_SCI_CMD 0x84
#define BATT_PORT 0x0a
#define BATT_LIMIT 0x21
#define BATT_FULL 0x41
CYB3R ★★★★★ ()
Ответ на: Так? от CYB3R

Не.

#define EC_SC 0x97
#define EC_DATA 0x93
#define IBF 1
#define OBF 0
#define EC_SC_READ_CMD 0x80
#define EC_SC_WRITE_CMD 0x81
#define EC_SC_SCI_CMD 0x84
#define BATT_PORT 0x0a
#define BATT_LIMIT 0x21
#define BATT_FULL 0x41

PoMbl4 ()
Ответ на: комментарий от PoMbl4

Окей, попробую, когда до ноута доберусь.

CYB3R ★★★★★ ()

Спасибо, помигал ледом на x230. Который порт за что отвечает где-нибудь задокументировано? Или совсем жуткая проприетарщина?

anonymous ()

Даже не знал, что есть такие фичи у ideapad'ов. Нужно будет установить венду и поиграться с этим lenovo energy management, может быть, тоже что-то интересное для себя откопаю.

У меня Z570, когда купил его, допиливал линуксовый драйвер ideapad-laptop для поддержки режимов вентилятора и сенсорных кнопок.

gentoo_root ★★★★★ ()
24 февраля 2015 г.

и смотрим какой байт меняется в окошке «Embedded Controller»

у меня много значений меняется, как найти нужные?

muted ()
Ответ на: комментарий от muted

Может быть у тебя там в этот момент скачут показания в регистрах всяких датчиков типа оборотов вентилятора и тому подобное? Попробуй потыркай и посмотри более внимательно. Попробуй с регистром, который на длительное время меняет своё значения после переключения режима батареии. И да, какой ноут?

PoMbl4 ()

А можно мне дамп DSDT твоего ноутбука? На моём Z570 запись 0x21 и 0x41 в регистр 0x0a ни к чему не приводит, венду установить нет возможности (нет сейчас свободного раздела), я хочу из твоего DSDT узнать, как называются нужные биты, и найти их адреса в моём DSDT (если вообще мой ноут поддерживает эту фичу).

Кстати, исходная программка fanctrl.c оказалась весьма прикольной, спасибо, я позапускал её на своём MSI Wind. Жаль, что если отрубить вентиляторы, он начинает нагреваться, так и не сделаю его бесшумным. Подумываю даже о том, чтобы запилить это управление вентилятором в драйвер msi-laptop (я уже добавлял туда некоторые штуки раньше).

gentoo_root ★★★★★ ()

#define BATT_LIMIT 0x10 #define BATT_FULL 0x40

ideapad z510 работает с этими значениями.

Супер, спасибо.

muted ()
Ответ на: комментарий от gentoo_root

http://rghost.net/78SHhsMyr содержимое /sys/firmware/acpi/tables/DSDT

Кстати попробуй значения юзера muted, может быть ноут близок к твоему.

Еще среди возможностей этого «lenovo energy management» есть продувка пыли (это когда вентилятор бешено воет) и калибровка батареии. Но эти две фичи не слишком полезные, пыль можно и так почистить, а батарея вроде как сама калибруется если её полностью зарядить и высадить в ноль. Продувку пыли я вроде пытался сделать, но ничего не вышло. Там как-то хитро меняются регистры в EC, видимо я недостаточно правдоподобно повторил эту последовательность.

PoMbl4 ()
Ответ на: комментарий от PoMbl4

http://rghost.net/78SHhsMyr содержимое /sys/firmware/acpi/tables/DSDT

Спасибо! Посмотрел туда, используемые биты называются BTCM и LBTM, а у меня в DSDT таких просто нет. Наверное, мой ноут это не поддерживает, либо эти биты по-другому называются. Надо попробовать через венду, если я найду возможность установить.

Кстати попробуй значения юзера muted, может быть ноут близок к твоему.

Пробовал, не работает, у меня по этим адресам ничего нет, даже запись не происходит в те биты.

Еще среди возможностей этого «lenovo energy management» есть продувка пыли (это когда вентилятор бешено воет)

На моём ноуте есть кнопка, которая под вендой меняет режимы вентилятора (4 режима всего). Когда я купил свой ноут, я написал патчи для драйвера ideapad-laptop, их приняли в ядро. На моём ноуте можно управлять вентилятором так:

echo 2 > /sys/bus/platform/drivers/ideapad_acpi/VPC2004:00/fan_mode

Допустимые значения описаны в /usr/src/linux/Documentation/ABI/testing/sysfs-platform-ideapad-laptop:

What:           /sys/devices/platform/ideapad/fan_mode
Date:           June 2012
KernelVersion:  3.6
Contact:        "Maxim Mikityanskiy <maxtram95@gmail.com>"
Description:
                Change fan mode
                There are four available modes:
                        * 0 -> Super Silent Mode
                        * 1 -> Standard Mode
                        * 2 -> Dust Cleaning
                        * 4 -> Efficient Thermal Dissipation Mode
gentoo_root ★★★★★ ()
Ответ на: комментарий от gentoo_root

Блин, таки мой ноут это тоже умеет! Сейчас заметил, что выше 50% не заряжается. До этого я фонарём установил второй бит (SMBM) в регистре 0x57. Скорее всего, это из-за этого. Снять бит не получается. Буду копать DSDT и пытаться через ACPI достучаться до этого механизма. Если найду нормальный способ управления этим, попытаюсь затолкать в драйвер ideapad-laptop.

gentoo_root ★★★★★ ()
Ответ на: комментарий от gentoo_root

Универсальный и более правильный способ управления батарейкой

Нужно использовать метод ACPI \_SB.PCI0.LPCB.EC0.VPC0.SBMC. Он принимает один параметр от 0 до 6. Пока что протестировать можно, используя модуль ядра acpi_call. Думаю, это должно работать на всех поддерживаемых леново.

Например, так я включаю режим зарядки до 50%:

echo '\_SB.PCI0.LPCB.EC0.VPC0.SBMC 4' > /proc/acpi/call
cat /proc/acpi/call | hexdump -C

А так я выключаю его:

echo '\_SB.PCI0.LPCB.EC0.VPC0.SBMC 5' > /proc/acpi/call
cat /proc/acpi/call | hexdump -C

PoMbl4, нужна помощь в тестировании. Я вижу в твоём DSDT, что каждое значение параметра от 0 до 6 что-то делает. На моём же ноуте имеют смысл только значения 0, 1, 4, 5. Значения 2, 3, 6 у меня ничего не делают. Попробуй повызывать по очереди метод SBMC с параметрами от 0 до 6 и посмотреть, что при этом происходит. Будет полезно знать всё, на что способен этот метод, я планирую эту функциональность попытаться добавить в драйвер ideapad-laptop.

gentoo_root ★★★★★ ()

Возможно, 1 — запустить калибровку батареи, 0 — прервать, потому что мне кажется, что после этого у меня начал врать индикатор времени. Сейчас попробую запустить и посмотреть, что из этого выйдет.

gentoo_root ★★★★★ ()

Хорошая новость, многие пользователи ideapad сокрушаются из-за отсутствия этого функционала в линукс. Завтра вечером постараюсь найти время и поставить этот модуль на мой Debian и попробовать разные значения.

PoMbl4 ()
Ответ на: комментарий от gentoo_root

Возможно, 1 — запустить калибровку батареи, 0 — прервать

Так и есть, нужно подключиться к сети и вызвать SBMC 1, после этого батарейка полностью зарядится, потом полностью разрядится, потом калибровка закончится, и батарейка станет снова заряжаться. В общем, всё как в венде: http://support.lenovo.com/ru/ru/documents/ht069480

gentoo_root ★★★★★ ()
Ответ на: комментарий от gentoo_root

Как-то так, смотрел результат с помощью вызова «acpi -V»:

debian:~# echo '\_SB.PCI0.LPCB.EC0.VPC0.SBMC 1' > /proc/acpi/call; cat /proc/acpi/call | hexdump -C
00000000  30 78 30 00 63 61 6c 6c  65 64 00                 |0x0.called.|
0000000b
Началась зарядка.

debian:~# echo '\_SB.PCI0.LPCB.EC0.VPC0.SBMC 0' > /proc/acpi/call; cat /proc/acpi/call | hexdump -C
00000000  30 78 30 00 63 61 6c 6c  65 64 00                 |0x0.called.|
0000000b
Зарядка отменена.

debian:~# echo '\_SB.PCI0.LPCB.EC0.VPC0.SBMC 2' > /proc/acpi/call; cat /proc/acpi/call | hexdump -C
00000000  30 78 30 00 63 61 6c 6c  65 64 00                 |0x0.called.|
0000000b
Похоже никакого эффекта.

debian:~# echo '\_SB.PCI0.LPCB.EC0.VPC0.SBMC 3' > /proc/acpi/call; cat /proc/acpi/call | hexdump -C
00000000  30 78 30 00 63 61 6c 6c  65 64 00                 |0x0.called.|
0000000b
Опять никакого эффекта.

debian:~# echo '\_SB.PCI0.LPCB.EC0.VPC0.SBMC 5' > /proc/acpi/call; cat /proc/acpi/call | hexdump -C
00000000  30 78 30 00 63 61 6c 6c  65 64 00                 |0x0.called.|
0000000b
Началась зарядка.

debian:~# echo '\_SB.PCI0.LPCB.EC0.VPC0.SBMC 4' > /proc/acpi/call; cat /proc/acpi/call | hexdump -C
00000000  30 78 30 00 63 61 6c 6c  65 64 00                 |0x0.called.|
0000000b
Разрядка.

debian:~# echo '\_SB.PCI0.LPCB.EC0.VPC0.SBMC 6' > /proc/acpi/call; cat /proc/acpi/call | hexdump -C
00000000  30 78 30 00 63 61 6c 6c  65 64 00                 |0x0.called.|
0000000b
Никакого эффекта.

PoMbl4 ()
Ответ на: комментарий от PoMbl4

Спасибо за тестирование!

смотрел результат с помощью вызова «acpi -V»:

У меня команда acpi может показывать неправду или Unknown, когда я начинаю управлять режимами. Было бы полезным после включения одного из режимов посидеть некоторое время и посмотреть, что реально происходит, а не просто опираться на выхлоп acpi -V.

Началась зарядка.

Зарядка отменена.

При воткнутом шнуре питания ноутбук перестал заряжаться?

Опять никакого эффекта.

Это очень странно, потому что насколько я вижу из твоего DSDT, у тебя 3 и 4 делают одно и то же.

Похоже никакого эффекта.

Никакого эффекта.

Это, конечно, досадно. 2 и 6 что-то всё же должны делать, но если эффект незаметен, это плохо, мы так и не узнаем, что они делают.

Если есть возможность и время, протестируй, пожалуйста, следующие вещи:

  • После записи 4 ноут входит в режим limited charge?
  • После записи 5 выходит из него?
  • После записи 1 начинается калибровка батареи? Ноут должен полностью зарядиться, разрядиться, после этого начать снова заряжаться как обычно. Для этого нужно время и постоянное подключение к шнуру питания.

Если случайно удастся выяснить, что делают 2 и 6, тоже буду рад услышать. 2 устанавливает бит CCSB, а 6 устанавливает бит CSBM, но что они делают, неизвестно. Также в DSDT нет метода для сброса этих битов. Может, они сбрасываются сами через время...

gentoo_root ★★★★★ ()
Ответ на: комментарий от gentoo_root

У меня команда acpi может показывать неправду или Unknown, когда я начинаю управлять режимами. Было бы полезным после включения одного из режимов посидеть некоторое время и посмотреть, что реально происходит, а не просто опираться на выхлоп acpi -V.

У меня она постоянно показывает «Unknown, 60%» в режиме защиты батареии т.е. заряд до 60%. Что она будет показывать при полном заряде не знаю, это нужно смотреть. При разрядке или зарядке она выводит адекватные сообщения.

При воткнутом шнуре питания ноутбук перестал заряжаться?

Да, шнур был воткнут. Он начал заряжаться до 100%, потом я это отменил и он разрядился до 60%.

Если есть возможность и время, протестируй, пожалуйста, следующие вещи

Проверю завтра или в выходные. Но тут скорее всего можно быть уверенным, в том, что 0/1 это управление калибровкой, а 5/4 переключение режима батареи.

PoMbl4 ()
9 июля 2015 г.

Как установить этот файл

Спасибо за найденное решение. Подкорректировал его в соответствии с данными RWEverything.

Подскажите пожалуйста чайнику - что делать дальше с этим файлом. Операционная система - Ubuntu.

anonymous ()
27 августа 2015 г.
Ответ на: комментарий от PoMbl4

Сделал скрипт авто-установки acpi_call модуля и скрипт для управление зарядом, гит Только для Lenovo G580(20150)

Deleted ()

Lenovo Y510P

Наткнулся на данную новость. Не до конца понял что и куда вписывать, но данные в Венде считал такие:

EC_SC/EC_DATA=0066/0062
___________________________
ячейка 00-01
значение «85» - запущен «Energy Manager»
значение «00» - закрыт «Energy Manager»
_________________________
ячейка 00-01
значение «03» - запущено «Удаление пыли»
значение «05» - завершено «Удаление пыли»
_________________________
ячейка 00-0A
значение «20» - 60% заряда
значение «40» - 100% заряда
___________________________

А вот со скриптами проблемы у меня. Планирую на Calculate Linux впиливать сие.

Huku111a ()
Ответ на: Lenovo Y510P от Huku111a

Ну для начала попробуй как-то так

#define BATT_LIMIT 0x21 заменить на 0x20
#define BATT_FULL 0x41 заменить на 0x40

Потом читай внимательно тред и делай это с помощью модуля ядра acpi_call.

PoMbl4 ()
Ответ на: комментарий от PoMbl4

Переполз на Calcualte Linux (Gentoo)

# emerge -av sys-power/acpi_call

# echo «modprobe acpi_call» > /etc/modules-load.d/acpi_call

и пользую скриптик battery_management.sh из архива :)

Всё отлично работает, спасибо!

Huku111a ()
Ответ на: Переполз на Calcualte Linux (Gentoo) от Huku111a

Скрипт этот

#!/bin/bash

echo If you do not run this script as Root it will not work

echo -n «Install the battery charge for 60% or 100%?(1/2)»

read item

case «$item» in

1)echo «Installing battery charge 60%»

echo '\_SB.PCI0.LPCB.EC0.VPC0.SBMC 4' > /proc/acpi/call

;;

2)echo «Installing battery charge 100%»

echo '\_SB.PCI0.LPCB.EC0.VPC0.SBMC 5' > /proc/acpi/call

;;

*) echo «Ничего не ввели. Ожидание ввода»

;;

esac

Huku111a ()

Как работать с программой, все данные совпадают. Она выдает дамп, или если задать параметры, пишет «unknown option»

anonymous ()
Ответ на: комментарий от anonymous

Она принимает в качестве аргументов либо f либо l, это сообщение она выдает если ты передаешь что-то отличное от f или l. А вообще лучше юзай acpi_call.

PoMbl4 ()
Ответ на: комментарий от PoMbl4

Спасибо огромное, все получилось :D

anonymous ()
8 декабря 2015 г.

Попытался работать через ACPI-call после установки соответствующей библиотеки на Ubuntu. Ноутбук - Yoga 2 Pro. ACPI-call выдает результат «Unknown parameter».

Под виндой посмотрел, что меняются сразу два байта: в случае включения режима консервирования батареи в регистре 0xEC - 0x20, в 0xED - 0x42, при нормальном режиме - 0xEC - 0x00, 0xED - 0x40. Полагаю, что необходимо переписывать программку выше и смотреть, влияет ли порядок записи регистров на включение/выключение консервации батареи?

http://imgur.com/a/BgXdQ

anonymous ()
Ответ на: комментарий от PoMbl4

Спасибо. Главный вопрос - значения EC_SC_READ_CMD, EC_SC_WRITE_CMD, EC_SC_SCI_CMD менять нужно? Даташита нема, а просто так куда угодно писать не хочется...

anonymous ()
Ответ на: комментарий от anonymous

Нет, это оставь как есть. Под куда угодно имелось в виду write_ec, в том смысле, что она принимает порт и значение.

PoMbl4 ()
Ответ на: комментарий от anonymous

Ну ты хоть отпишись, что да как. Влепи свою редакцию проги для своей ёги. А то народ уже который год трахается с ideapad.

PoMbl4 ()
Ответ на: комментарий от PoMbl4

Добавил включение режима быстрой зарядки аккумулятора и зарядку от USB когда ноутбук выключен. Лежит тут: https://gitlab.com/mikoff/Y2P-PM/

anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.