From: Wu Zhangjin <wuzhangjin@xxxxxxxxx> This patch adds APM emulated Battery Driver, it provides standard interface(/proc/apm) for user-space applications(e.g. kpowersave, gnome-power-manager) to manage the battery. Signed-off-by: Wu Zhangjin <wuzhangjin@xxxxxxxxx> --- drivers/platform/mips/Kconfig | 2 + drivers/platform/mips/yeeloong_laptop.c | 108 ++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig index c1ba03d..965933b 100644 --- a/drivers/platform/mips/Kconfig +++ b/drivers/platform/mips/Kconfig @@ -18,6 +18,8 @@ config LEMOTE_YEELOONG2F tristate "Lemote YeeLoong Laptop" depends on LEMOTE_MACH2F select BACKLIGHT_CLASS_DEVICE + select SYS_SUPPORTS_APM_EMULATION + select APM_EMULATION help YeeLoong netbook is a mini laptop made by Lemote, which is basically compatible to FuLoong2F mini PC, but it has an extra Embedded diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c index f04a7e2..0d9f2a6 100644 --- a/drivers/platform/mips/yeeloong_laptop.c +++ b/drivers/platform/mips/yeeloong_laptop.c @@ -2,7 +2,7 @@ * Driver for YeeLoong laptop extras * * Copyright (C) 2009 Lemote Inc. - * Author: Wu Zhangjin <wuzj@xxxxxxxxxx> + * Author: Wu Zhangjin <wuzj@xxxxxxxxxx>, Liu Junliang <liujl@xxxxxxxxxx> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -13,6 +13,7 @@ #include <linux/platform_device.h> #include <linux/backlight.h> /* for backlight subdriver */ #include <linux/fb.h> +#include <linux/apm-emulation.h>/* for battery subdriver */ #include <ec_kb3310b.h> @@ -85,6 +86,106 @@ static void yeeloong_backlight_exit(void) } } +/* battery subdriver */ + +static void get_fixed_battery_info(void) +{ + int design_cap, full_charged_cap, design_vol, vendor, cell_count; + + design_cap = (ec_read(REG_BAT_DESIGN_CAP_HIGH) << 8) + | ec_read(REG_BAT_DESIGN_CAP_LOW); + full_charged_cap = (ec_read(REG_BAT_FULLCHG_CAP_HIGH) << 8) + | ec_read(REG_BAT_FULLCHG_CAP_LOW); + design_vol = (ec_read(REG_BAT_DESIGN_VOL_HIGH) << 8) + | ec_read(REG_BAT_DESIGN_VOL_LOW); + vendor = ec_read(REG_BAT_VENDOR); + cell_count = ec_read(REG_BAT_CELL_COUNT); + + if (vendor != 0) { + pr_info("battery vendor(%s), cells count(%d), " + "with designed capacity(%d),designed voltage(%d)," + " full charged capacity(%d)\n", + (vendor == + FLAG_BAT_VENDOR_SANYO) ? "SANYO" : "SIMPLO", + (cell_count == FLAG_BAT_CELL_3S1P) ? 3 : 6, + design_cap, design_vol, + full_charged_cap); + } +} + +#define APM_CRITICAL 5 + +static void get_power_status(struct apm_power_info *info) +{ + unsigned char bat_status; + + info->battery_status = APM_BATTERY_STATUS_UNKNOWN; + info->battery_flag = APM_BATTERY_FLAG_UNKNOWN; + info->units = APM_UNITS_MINS; + + info->battery_life = (ec_read(REG_BAT_RELATIVE_CAP_HIGH) << 8) | + (ec_read(REG_BAT_RELATIVE_CAP_LOW)); + + info->ac_line_status = (ec_read(REG_BAT_POWER) & BIT_BAT_POWER_ACIN) ? + APM_AC_ONLINE : APM_AC_OFFLINE; + + bat_status = ec_read(REG_BAT_STATUS); + + if (!(bat_status & BIT_BAT_STATUS_IN)) { + /* no battery inserted */ + info->battery_status = APM_BATTERY_STATUS_NOT_PRESENT; + info->battery_flag = APM_BATTERY_FLAG_NOT_PRESENT; + info->time = 0x00; + return; + } + + /* adapter inserted */ + if (info->ac_line_status == APM_AC_ONLINE) { + if (!(bat_status & BIT_BAT_STATUS_FULL)) { + /* battery is not fully charged */ + info->battery_status = APM_BATTERY_STATUS_CHARGING; + info->battery_flag = APM_BATTERY_FLAG_CHARGING; + } else { + /* battery is fully charged */ + info->battery_status = APM_BATTERY_STATUS_HIGH; + info->battery_flag = APM_BATTERY_FLAG_HIGH; + info->battery_life = 100; + } + } else { + /* battery is too low */ + if (bat_status & BIT_BAT_STATUS_LOW) { + info->battery_status = APM_BATTERY_STATUS_LOW; + info->battery_flag = APM_BATTERY_FLAG_LOW; + if (info->battery_life <= APM_CRITICAL) { + /* we should power off the system now */ + info->battery_status = + APM_BATTERY_STATUS_CRITICAL; + info->battery_flag = APM_BATTERY_FLAG_CRITICAL; + } + } else { + /* assume the battery is high enough. */ + info->battery_status = APM_BATTERY_STATUS_HIGH; + info->battery_flag = APM_BATTERY_FLAG_HIGH; + } + } + info->time = ((info->battery_life - 3) * 54 + 142) / 60; +} + +static int yeeloong_battery_init(void) +{ + get_fixed_battery_info(); + + apm_get_power_status = get_power_status; + + return 0; +} + +static void yeeloong_battery_exit(void) +{ + if (apm_get_power_status == get_power_status) + apm_get_power_status = NULL; +} + static struct platform_device_id platform_device_ids[] = { { .name = "yeeloong_laptop", @@ -122,11 +223,14 @@ static int __init yeeloong_init(void) return ret; } + yeeloong_battery_init(); + return 0; } static void __exit yeeloong_exit(void) { + yeeloong_battery_exit(); yeeloong_backlight_exit(); platform_driver_unregister(&platform_driver); @@ -136,6 +240,6 @@ static void __exit yeeloong_exit(void) module_init(yeeloong_init); module_exit(yeeloong_exit); -MODULE_AUTHOR("Wu Zhangjin <wuzj@xxxxxxxxxx>"); +MODULE_AUTHOR("Wu Zhangjin <wuzj@xxxxxxxxxx>; Liu Junliang <liujl@xxxxxxxxxx>"); MODULE_DESCRIPTION("YeeLoong laptop driver"); MODULE_LICENSE("GPL"); -- 1.6.2.1