Changes from old revision: o Fixes the bugs In the old driver, it didn't show the current charging status and reported the wrong full charge and voltage value. Capacity = Charge_full_last (Dynamic) / Charge_full_design (Fixed) Percentage Charge = Charge_now (Dyn-Dynamic) / Charge_full_last (Dynamic) Based on the old emulated APM battery driver and the power_supply class, this patch adds a new battery driver. References: 1. Documentation/power/power_supply_class.txt 2. drivers/power/ Signed-off-by: Liu Shiwei Signed-off-by: Wu Zhangjin --- drivers/platform/mips/Kconfig | 1 + drivers/platform/mips/yeeloong_laptop.c | 228 +++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+), 0 deletions(-) diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig index 01560b0..cdfccea 100644 --- a/drivers/platform/mips/Kconfig +++ b/drivers/platform/mips/Kconfig @@ -21,6 +21,7 @@ config LEMOTE_YEELOONG2F select HWMON select VIDEO_OUTPUT_CONTROL select INPUT_SPARSEKMAP + select POWER_SUPPLY depends on INPUT help YeeLoong netbook is a mini laptop made by Lemote, which is basically diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c index 817e8fc..e321c58 100644 --- a/drivers/platform/mips/yeeloong_laptop.c +++ b/drivers/platform/mips/yeeloong_laptop.c @@ -19,6 +19,7 @@ #include #include #include +#include /* for AC & Battery subdriver */ #include @@ -330,6 +331,213 @@ static void yeeloong_hwmon_exit(void) } } +/* AC & Battery subdriver */ + +static struct power_supply yeeloong_ac, yeeloong_bat; + +#define AC_OFFLINE 0 +#define AC_ONLINE 1 + +static int yeeloong_get_ac_props(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = ((ec_read(REG_BAT_POWER)) & BIT_BAT_POWER_ACIN) ? + AC_ONLINE : AC_OFFLINE; + break; + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property yeeloong_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static struct power_supply yeeloong_ac = { + .name = "yeeloong-ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = yeeloong_ac_props, + .num_properties = ARRAY_SIZE(yeeloong_ac_props), + .get_property = yeeloong_get_ac_props, +}; + +#define BAT_CAP_CRITICAL 5 +#define BAT_CAP_HIGH 99 + +static int yeeloong_bat_get_ex_property(enum power_supply_property psp, + union power_supply_propval *val) +{ + int bat_in, curr_cap, cap_level, status, charge, health; + + status = ec_read(REG_BAT_STATUS); + bat_in = status & BIT_BAT_STATUS_IN; + curr_cap = get_bat_info(RELATIVE_CAP); + if (status & BIT_BAT_STATUS_FULL) + curr_cap = 100; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bat_in; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = curr_cap; + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + cap_level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + if (status & BIT_BAT_STATUS_LOW) { + cap_level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + if (curr_cap <= BAT_CAP_CRITICAL) + cap_level = + POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + } else if (status & BIT_BAT_STATUS_FULL) { + cap_level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + if (curr_cap >= BAT_CAP_HIGH) + cap_level = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; + } else if (status & BIT_BAT_STATUS_DESTROY) + cap_level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; + val->intval = cap_level; + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + /* seconds */ + val->intval = bat_in ? (curr_cap - 3) * 54 + 142 : 0; + break; + case POWER_SUPPLY_PROP_STATUS: + if (!bat_in) + charge = POWER_SUPPLY_STATUS_UNKNOWN; + else { + if (status & BIT_BAT_STATUS_FULL) { + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + } + + charge = ec_read(REG_BAT_CHARGE); + if (charge & FLAG_BAT_CHARGE_DISCHARGE) + charge = POWER_SUPPLY_STATUS_DISCHARGING; + else if (charge & FLAG_BAT_CHARGE_CHARGE) + charge = POWER_SUPPLY_STATUS_CHARGING; + else + charge = POWER_SUPPLY_STATUS_NOT_CHARGING; + } + val->intval = charge; + break; + case POWER_SUPPLY_PROP_HEALTH: + if (!bat_in) /* no battery present */ + health = POWER_SUPPLY_HEALTH_UNKNOWN; + else { /* Assume it is good */ + health = POWER_SUPPLY_HEALTH_GOOD; + if (status & + (BIT_BAT_STATUS_DESTROY | BIT_BAT_STATUS_LOW)) + health = POWER_SUPPLY_HEALTH_DEAD; + if (ec_read(REG_BAT_CHARGE_STATUS) & + BIT_BAT_CHARGE_STATUS_OVERTEMP) + health = POWER_SUPPLY_HEALTH_OVERHEAT; + } + val->intval = health; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: /* 1/100(%)*1000 µAh */ + val->intval = curr_cap * get_bat_info(FULLCHG_CAP) * 10; + break; + default: + return -EINVAL; + } + return 0; +} + +static int yeeloong_get_bat_props(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + /* Fixed information */ + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = get_bat_info(DESIGN_VOL) * 1000; /* mV -> µV */ + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = get_bat_info(DESIGN_CAP) * 1000; /*mAh -> µAh*/ + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = get_bat_info(FULLCHG_CAP) * 1000; /* µAh */ + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = + (ec_read(REG_BAT_VENDOR) == FLAG_BAT_VENDOR_SANYO) ? + "SANYO" : "SIMPLO"; + break; + /* Dynamic information */ + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = get_battery_current() * 1000; /* mA -> µA */ + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = get_battery_voltage() * 1000; /* mV -> µV */ + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = get_battery_temp(); /* Celcius */ + break; + /* Dynamic but relative information */ + default: + return yeeloong_bat_get_ex_property(psp, val); + } + + return 0; +} + +static enum power_supply_property yeeloong_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static struct power_supply yeeloong_bat = { + .name = "yeeloong-bat", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = yeeloong_bat_props, + .num_properties = ARRAY_SIZE(yeeloong_bat_props), + .get_property = yeeloong_get_bat_props, +}; + +static int ac_bat_initialized; + +static int yeeloong_bat_init(void) +{ + int ret; + + ret = power_supply_register(NULL, &yeeloong_ac); + if (ret) + return ret; + ret = power_supply_register(NULL, &yeeloong_bat); + if (ret) { + power_supply_unregister(&yeeloong_ac); + return ret; + } + ac_bat_initialized = 1; + + return 0; +} + +static void yeeloong_bat_exit(void) +{ + ac_bat_initialized = 0; + + power_supply_unregister(&yeeloong_ac); + power_supply_unregister(&yeeloong_bat); +} + /* video output subdriver */ static int lcd_video_output_get(struct output_device *od) @@ -602,6 +810,15 @@ static int usb0_handler(int status) return status; } +static int ac_bat_handler(int status) +{ + if (ac_bat_initialized) { + power_supply_changed(&yeeloong_ac); + power_supply_changed(&yeeloong_bat); + } + return status; +} + static void do_event_action(int event) { sci_handler handler; @@ -647,6 +864,9 @@ static void do_event_action(int event) case EVENT_AUDIO_VOLUME: reg = REG_AUDIO_VOLUME; break; + case EVENT_AC_BAT: + handler = ac_bat_handler; + break; default: break; } @@ -903,6 +1123,13 @@ static int __init yeeloong_init(void) return ret; } + ret = yeeloong_bat_init(); + if (ret) { + pr_err("Fail to register yeeloong battery driver.\n"); + yeeloong_bat_exit(); + return ret; + } + ret = yeeloong_hwmon_init(); if (ret) { pr_err("Fail to register yeeloong hwmon driver.\n"); @@ -932,6 +1159,7 @@ static void __exit yeeloong_exit(void) yeeloong_hotkey_exit(); yeeloong_vo_exit(); yeeloong_hwmon_exit(); + yeeloong_bat_exit(); yeeloong_backlight_exit(); platform_driver_unregister(&platform_driver); -- 1.7.0.4