diff options
Diffstat (limited to 'drivers/power/sec_battery_px.c')
-rw-r--r-- | drivers/power/sec_battery_px.c | 206 |
1 files changed, 191 insertions, 15 deletions
diff --git a/drivers/power/sec_battery_px.c b/drivers/power/sec_battery_px.c index 0b5d8d4..4787c9d 100644 --- a/drivers/power/sec_battery_px.c +++ b/drivers/power/sec_battery_px.c @@ -33,13 +33,18 @@ #include <linux/power/sec_battery_px.h> #include <linux/power/max17042_fuelgauge_px.h> #include <linux/mfd/max8997.h> - +#if defined(CONFIG_TARGET_LOCALE_KOR) +#include <linux/proc_fs.h> +#endif /* CONFIG_TARGET_LOCALE_KOR */ #include <plat/adc.h> #include <mach/usb_switch.h> #define FAST_POLL 40 /* 40 sec */ #define SLOW_POLL (30*60) /* 30 min */ +/* cut off voltage */ +#define MAX_CUT_OFF_VOL 3500 + /* SIOP */ #define CHARGING_CURRENT_HIGH_LOW_STANDARD 450 #define CHARGING_CURRENT_USB 500 @@ -67,11 +72,16 @@ static char *supply_list[] = { "battery", }; +/* Get LP charging mode state */ +unsigned int lpcharge; + static enum power_supply_property sec_battery_properties[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_CAPACITY, }; @@ -95,6 +105,11 @@ struct battery_info { u32 batt_improper_ta; /* 1: improper ta */ u32 abstimer_is_active; /* 0 : Not active 1: Active */ u32 siop_activated; /* 0 : Not active 1: Active */ +#if defined(CONFIG_TARGET_LOCALE_KOR) + u32 batt_vfsoc; + u32 batt_soc; +#endif /* CONFIG_TARGET_LOCALE_KOR */ + u32 batt_current_avg; }; struct battery_data { @@ -123,6 +138,9 @@ struct battery_data { #ifdef __TEST_DEVICE_DRIVER__ struct wake_lock wake_lock_for_dev; #endif /* __TEST_DEVICE_DRIVER__ */ +#if defined(CONFIG_TARGET_LOCALE_KOR) + struct proc_dir_entry *entry; +#endif /* CONFIG_TARGET_LOCALE_KOR */ enum charger_type current_cable_status; enum charger_type previous_cable_status; int present; @@ -146,6 +164,7 @@ struct battery_data { /* 0:Default, 1:Only charger, 2:Only PMIC */ int cable_detect_source; int pmic_cable_state; + int dock_type; bool is_low_batt_alarm; }; @@ -174,6 +193,7 @@ static int bat_temp_force_state; static bool check_UV_charging_case(void); static void sec_bat_status_update(struct power_supply *bat_ps); +static void fullcharge_discharge_comp(struct battery_data *battery); static int get_cached_charging_status(struct battery_data *battery) { @@ -226,7 +246,7 @@ static int check_ta_conn(struct battery_data *battery) #ifdef CONFIG_SAMSUNG_LPM_MODE static void lpm_mode_check(struct battery_data *battery) { - battery->charging_mode_booting = \ + battery->charging_mode_booting = lpcharge = battery->pdata->check_lp_charging_boot(); pr_info("%s : charging_mode_booting(%d)\n", __func__, battery->charging_mode_booting); @@ -292,7 +312,7 @@ enum charger_type sec_get_dedicted_charger_type(struct battery_data *battery) mutex_lock(&battery->work_lock); /* ADC check margin (300~500ms) */ - msleep(150); + msleep(300); usb_switch_lock(); usb_switch_set_path(USB_PATH_ADCCHECK); @@ -318,10 +338,13 @@ enum charger_type sec_get_dedicted_charger_type(struct battery_data *battery) battery->pdata->charger.accessory_line); pr_info("%s: accessory line(%d)\n", __func__, accessory_line); - if (accessory_line == 0) /* HDMI dock cable connected*/ + if (battery->dock_type == DOCK_DESK) { + pr_info("%s: deskdock(%d) charging\n", + __func__, battery->dock_type); result = CHARGER_DOCK; - else + } else { result = CHARGER_AC; + } #else result = CHARGER_AC; /* TA connected. */ #endif @@ -369,6 +392,9 @@ static void sec_TA_work_handler(struct work_struct *work) /* Prevent unstable VBUS signal from PC */ ta_state = check_ta_conn(battery); if (ta_state == 0) { + /* Check for immediate discharge after fullcharge */ + fullcharge_discharge_comp(battery); + msleep(TA_DISCONNECT_RECHECK_TIME); if (ta_state != check_ta_conn(battery)) { pr_info("%s: unstable ta_state(%d), ignore it.\n", @@ -456,6 +482,29 @@ static int is_over_abs_time(struct battery_data *battery) return 0; } +/* fullcharge_discharge_comp: During the small window between + * Full Charge and Recharge if the cable is connected , we always show + * 100% SOC to the UI , evven though in background its discharging. + * If the user pulls out the TA in between this small window he should + * see a sudden drop from 100% to a disacharged state of 95% - 98%. + * We fake the SOC on Fulll cap and let FG manage the differences on + * long run. + * Also during the start of recharging phase if the TA is disconnected SOC + * can sudden;y drop down to a lower value. In that case also the user + * expects to see a 100% so we update Full Cap again. +*/ +static void fullcharge_discharge_comp(struct battery_data *battery) +{ + int fg_vcell = get_fuelgauge_value(FG_VOLTAGE); + + if (battery->info.batt_is_full) { + pr_info("%s: Resetting FULLCAP !!\n", __func__); + fg_reset_fullcap_in_fullcharge(); + } + + return; +} + static int sec_get_bat_level(struct power_supply *bat_ps) { struct battery_data *battery = container_of(bat_ps, @@ -476,6 +525,9 @@ static int sec_get_bat_level(struct power_supply *bat_ps) } fg_soc = get_fuelgauge_value(FG_LEVEL); +#if defined(CONFIG_TARGET_LOCALE_KOR) + battery->info.batt_soc = fg_soc; +#endif /* CONFIG_TARGET_LOCALE_KOR */ if (fg_soc < 0) { pr_info("Can't read soc!!!"); fg_soc = battery->info.level; @@ -523,6 +575,10 @@ static int sec_get_bat_level(struct power_supply *bat_ps) battery->info.batt_current = fg_current; avg_current = get_fuelgauge_value(FG_CURRENT_AVG); fg_vfsoc = get_fuelgauge_value(FG_VF_SOC); +#if defined(CONFIG_TARGET_LOCALE_KOR) + battery->info.batt_vfsoc = fg_vfsoc; +#endif /* CONFIG_TARGET_LOCALE_KOR */ + battery->info.batt_current_avg = avg_current; /* P4-Creative does not set full flag by force */ #if !defined(CONFIG_MACH_P4NOTE) @@ -992,8 +1048,7 @@ static int sec_bat_get_charging_status(struct battery_data *battery) case CHARGER_AC: case CHARGER_MISC: case CHARGER_DOCK: - if (battery->info.batt_is_full || - battery->info.level == 100) + if (battery->info.batt_is_full) return POWER_SUPPLY_STATUS_FULL; else if (battery->info.batt_improper_ta) return POWER_SUPPLY_STATUS_DISCHARGING; @@ -1015,6 +1070,15 @@ static int sec_bat_set_property(struct power_supply *ps, switch (psp) { case POWER_SUPPLY_PROP_ONLINE: +#if defined(CONFIG_MACH_P4NOTE) + battery->dock_type = val->intval; + pr_info("dock type(%d)\n", battery->dock_type); + /* if need current update state, set */ + if (battery->dock_type == DOCK_DESK) { + sec_get_cable_status(battery); + sec_cable_changed(battery); + } +#else battery->pmic_cable_state = val->intval; pr_info("PMIC cable state: %d\n", battery->pmic_cable_state); if (battery->cable_detect_source == 2) { @@ -1024,6 +1088,7 @@ static int sec_bat_set_property(struct power_supply *ps, sec_cable_changed(battery); } battery->cable_detect_source = 0; +#endif break; default: return -EINVAL; @@ -1052,8 +1117,25 @@ static int sec_bat_get_property(struct power_supply *bat_ps, case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = battery->info.batt_current; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + val->intval = battery->info.batt_current_avg; + break; case POWER_SUPPLY_PROP_CAPACITY: +#if defined(CONFIG_MACH_P4NOTE) + if ((battery->info.level == 0) && + (battery->info.batt_vol > MAX_CUT_OFF_VOL)) { + pr_info("%s: mismatch power off soc(%d) and vol(%d)\n", + __func__, battery->info.level, battery->info.batt_vol); + val->intval = battery->info.level = 1; + } else { + val->intval = battery->info.level; + } +#else val->intval = battery->info.level; +#endif pr_info("level = %d\n", val->intval); break; case POWER_SUPPLY_PROP_TEMP: @@ -1143,6 +1225,13 @@ static struct device_attribute sec_battery_attrs[] = { #endif SEC_BATTERY_ATTR(jig_on), SEC_BATTERY_ATTR(fg_capacity), + SEC_BATTERY_ATTR(update), +#if defined(CONFIG_TARGET_LOCALE_KOR) + SEC_BATTERY_ATTR(batt_sysrev), + SEC_BATTERY_ATTR(batt_temp_adc_spec), + SEC_BATTERY_ATTR(batt_current_adc), + SEC_BATTERY_ATTR(batt_current_avg), +#endif /* CONFIG_TARGET_LOCALE_KOR */ }; enum { @@ -1167,6 +1256,13 @@ enum { #endif JIG_ON, FG_CAPACITY, + UPDATE, +#if defined(CONFIG_TARGET_LOCALE_KOR) + BATT_SYSTEM_REVISION, + BATT_TEMP_ADC_SPEC, + BATT_CURRENT_ADC, + BATT_CURRENT_AVG, +#endif /* CONFIG_TARGET_LOCALE_KOR */ }; static int sec_bat_create_attrs(struct device *dev) @@ -1268,6 +1364,28 @@ static ssize_t sec_bat_show_property(struct device *dev, get_fuelgauge_capacity(CAPACITY_TYPE_AV), get_fuelgauge_capacity(CAPACITY_TYPE_REP)); break; +#if defined(CONFIG_TARGET_LOCALE_KOR) + case BATT_SYSTEM_REVISION: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + system_rev); + break; + case BATT_TEMP_ADC_SPEC: + i += scnprintf(buf + i, PAGE_SIZE - i, + "(HIGH: %d / %d,\tLOW: %d / %d)\n", + debug_batterydata->pdata->temp_high_threshold / 100, + debug_batterydata->pdata->temp_high_recovery / 100, + debug_batterydata->pdata->temp_low_threshold / 100, + debug_batterydata->pdata->temp_low_recovery / 100); + break; + case BATT_CURRENT_ADC: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + get_fuelgauge_value(FG_CURRENT)); + break; + case BATT_CURRENT_AVG: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + get_fuelgauge_value(FG_CURRENT_AVG)); + break; +#endif /* CONFIG_TARGET_LOCALE_KOR */ default: i = -EINVAL; } @@ -1334,6 +1452,12 @@ static ssize_t sec_bat_store(struct device *dev, } pr_info("%s: SIOP_ACTIVATED :%d\n", __func__, x); break; + case UPDATE: + pr_info("%s: battery update\n", __func__); + wake_lock(&debug_batterydata->work_wake_lock); + schedule_work(&debug_batterydata->battery_work); + ret = count; + break; #ifdef CONFIG_SAMSUNG_LPM_MODE case BATT_LP_CHARGING: if (sscanf(buf, "%d\n", &x) == 1) { @@ -1503,8 +1627,6 @@ static int sec_cable_status_update(struct battery_data *battery, int status) wake_lock(&battery->work_wake_lock); schedule_work(&battery->battery_work); - pr_debug("call power_supply_changed "); - return ret; } @@ -1543,10 +1665,12 @@ static void sec_bat_status_update(struct power_supply *bat_ps) power_supply_changed(bat_ps); pr_debug("call power_supply_changed"); - pr_info("BAT : soc(%d), vcell(%dmV), curr(%dmA), temp(%d.%d), chg(%d)", + pr_info("BAT : soc(%d), vcell(%dmV), curr(%dmA), " + "avg curr(%dmA), temp(%d.%d), chg(%d)", battery->info.level, battery->info.batt_vol, battery->info.batt_current, + battery->info.batt_current_avg, battery->info.batt_temp/10, battery->info.batt_temp%10, battery->info.charging_enabled); @@ -1723,17 +1847,15 @@ void fuelgauge_recovery_handler(struct work_struct *work) if (battery->info.level > 0) { pr_err("%s: Reduce the Reported SOC by 1 unit, wait for 30s\n", - __func__); + __func__); if (!battery->info.charging_enabled) wake_lock_timeout(&battery->vbus_wake_lock, HZ); current_soc = get_fuelgauge_value(FG_LEVEL); if (current_soc) { pr_info("%s: Returning to Normal discharge path.\n", __func__); - pr_info(" Actual SOC(%d) non-zero.\n", - current_soc); + pr_info(" Actual SOC(%d) non-zero.\n", current_soc); battery->is_low_batt_alarm = false; - return; } else { battery->info.level--; pr_err("%s: New Reduced Reported SOC (%d).\n", @@ -1745,12 +1867,22 @@ void fuelgauge_recovery_handler(struct work_struct *work) } } else { if (!get_charger_status(battery)) { - pr_err("Set battery level as 0, power off.\n"); + pr_err("%s: 0%% wo/ charging, will be power off\n", + __func__); battery->info.level = 0; wake_lock_timeout(&battery->vbus_wake_lock, HZ); power_supply_changed(&battery->psy_battery); + } else { + pr_info("%s: 0%% w/ charging, exit low bat alarm\n", + __func__); + /* finish low battery alarm state */ + battery->is_low_batt_alarm = false; } } + + pr_info("%s: low batt alarm(%d)\n", __func__, + battery->is_low_batt_alarm); + return; } #define STABLE_LOW_BATTERY_DIFF 3 @@ -1759,6 +1891,7 @@ int _low_battery_alarm_(struct battery_data *battery) { int overcurrent_limit_in_soc; int current_soc = get_fuelgauge_value(FG_LEVEL); + pr_info("%s\n", __func__); if (battery->info.level <= STABLE_LOW_BATTERY_DIFF) overcurrent_limit_in_soc = STABLE_LOW_BATTERY_DIFF_LOWBATT; @@ -1881,6 +2014,39 @@ static void fullcharging_work_handler(struct work_struct *work) enable_irq(battery->charging_irq); } +#if defined(CONFIG_TARGET_LOCALE_KOR) +static int sec_bat_read_proc(char *buf, char **start, + off_t offset, int count, int *eof, void *data) +{ + struct battery_data *battery = data; + struct timespec cur_time; + ktime_t ktime; + int len = 0; + + ktime = alarm_get_elapsed_realtime(); + cur_time = ktime_to_timespec(ktime); + + len = sprintf(buf, + "%lu\t%u\t%u\t%u\t%u\t%u\t%u\t%d\t%d\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t0x%04x\t0x%04x\n", + cur_time.tv_sec, + battery->info.batt_vol, battery->info.level, + battery->info.batt_soc, battery->info.batt_vfsoc, + max17042_chip_data->info.psoc, + battery->info.batt_temp, + battery->info.batt_current, battery->info.batt_current_avg, + battery->info.charging_source, battery->info.batt_improper_ta, + battery->info.charging_enabled, battery->info.charging_current, + sec_bat_get_charging_status(battery), battery->info.batt_health, + battery->info.batt_is_full, battery->info.batt_is_recharging, + battery->info.abstimer_is_active, battery->info.siop_activated, + get_fuelgauge_capacity(CAPACITY_TYPE_FULL), + get_fuelgauge_capacity(CAPACITY_TYPE_REP) + ); + + return len; +} +#endif /* CONFIG_TARGET_LOCALE_KOR */ + static int __devinit sec_bat_probe(struct platform_device *pdev) { struct sec_battery_platform_data *pdata = dev_get_platdata(&pdev->dev); @@ -2063,6 +2229,16 @@ static int __devinit sec_bat_probe(struct platform_device *pdev) lpm_mode_check(battery); #endif +#if defined(CONFIG_TARGET_LOCALE_KOR) + battery->entry = create_proc_entry("batt_info_proc", S_IRUGO, NULL); + if (!battery->entry) { + pr_err("%s : failed to create proc_entry!\n", __func__); + } else { + battery->entry->read_proc = sec_bat_read_proc; + battery->entry->data = battery; + } +#endif /* CONFIG_TARGET_LOCALE_KOR */ + return 0; err_charger_irq: |