From 559cc5d4e766327be6e8965bd88131fc95eb919b Mon Sep 17 00:00:00 2001 From: ananjaser1211 Date: Mon, 22 Jun 2020 13:09:39 +0400 Subject: [PATCH] drivers: battery: revert back to stock Oreo battery charger * Remove custom store mode as its causing issues ill explain in the next commit * Add prevent Swelling and battery health checking support Signed-off-by: ananjaser1211 --- drivers/battery/Kconfig | 5 + drivers/battery/Makefile | 1 + drivers/battery/sec_battery.c | 200 +++-- drivers/battery/sec_psb.c | 764 ++++++++++++++++++++ include/linux/battery/sec_battery.h | 24 +- include/linux/battery/sec_charging_common.h | 13 + include/linux/battery/sec_psb.h | 36 + 7 files changed, 988 insertions(+), 55 deletions(-) create mode 100644 drivers/battery/sec_psb.c create mode 100644 include/linux/battery/sec_psb.h diff --git a/drivers/battery/Kconfig b/drivers/battery/Kconfig index 362e768c1cf5..32d2cae93e03 100755 --- a/drivers/battery/Kconfig +++ b/drivers/battery/Kconfig @@ -47,6 +47,11 @@ config BATTERY_CISD Say Y to include support for cisd cisd means cell internal short detection + config PREVENT_SWELLING_BATTERY + bool "support for psb" + help + Say Y to include support for psb + config UPDATE_BATTERY_DATA bool "support for updating battery data" default n diff --git a/drivers/battery/Makefile b/drivers/battery/Makefile index 8d12d0fa0312..af7535458b4c 100755 --- a/drivers/battery/Makefile +++ b/drivers/battery/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_STEP_CHARGING) += sec_step_charging.o obj-$(CONFIG_BATTERY_CISD) += sec_cisd.o obj-$(CONFIG_UPDATE_BATTERY_DATA) += sec_battery_data.o obj-$(CONFIG_BATTERY_NOTIFIER) += battery_notifier.o +obj-$(CONFIG_PREVENT_SWELLING_BATTERY) += sec_psb.o obj-$(CONFIG_FUELGAUGE_MAX17042) += max17042_fuelgauge.o sec_fuelgauge.o obj-$(CONFIG_FUELGAUGE_MAX17048) += max17048_fuelgauge.o sec_fuelgauge.o diff --git a/drivers/battery/sec_battery.c b/drivers/battery/sec_battery.c index 8902eb1f6879..ee32c9fd6da4 100644 --- a/drivers/battery/sec_battery.c +++ b/drivers/battery/sec_battery.c @@ -18,6 +18,10 @@ static int wl_polling = 10; module_param(wl_polling, int, 0644); +#if defined(CONFIG_PREVENT_SWELLING_BATTERY) +#include +#endif + enum { P9220_VOUT_0V = 0, P9220_VOUT_5V, @@ -39,14 +43,6 @@ enum { #define SEC_INPUT_VOLTAGE_5V 5 #define SEC_INPUT_VOLTAGE_9V 9 -static unsigned int STORE_MODE_CHARGING_MAX = 90; -static unsigned int STORE_MODE_CHARGING_MIN = 20; - -module_param_named(store_mode_max, STORE_MODE_CHARGING_MAX, uint, S_IWUSR | S_IRUGO); -module_param_named(store_mode_min, STORE_MODE_CHARGING_MIN, uint, S_IWUSR | S_IRUGO); - -const char *charger_chip_name; - bool sleep_mode = false; static struct device_attribute sec_battery_attrs[] = { @@ -602,9 +598,7 @@ static int sec_bat_set_charging_current(struct sec_battery_info *battery) return 0; } -static int sec_bat_set_charge( - struct sec_battery_info *battery, - int chg_mode) +int sec_bat_set_charge(struct sec_battery_info *battery, int chg_mode) { union power_supply_propval val; ktime_t current_time; @@ -656,17 +650,16 @@ static int sec_bat_set_charge( return 0; } -static void sec_bat_set_misc_event(struct sec_battery_info *battery, - const int misc_event_type, bool do_clear) { +void sec_bat_set_misc_event(struct sec_battery_info *battery, + unsigned int misc_event_val, unsigned int misc_event_mask) +{ + unsigned int temp = battery->misc_event; mutex_lock(&battery->misclock); - pr_info("%s: %s misc event(now=0x%x, value=0x%x)\n", - __func__, ((do_clear) ? "clear" : "set"), battery->misc_event, misc_event_type); - if (do_clear) { - battery->misc_event &= ~misc_event_type; - } else { - battery->misc_event |= misc_event_type; - } + battery->misc_event &= ~misc_event_mask; + battery->misc_event |= misc_event_val; + pr_info("%s: misc event before(0x%x), after(0x%x)\n", + __func__, temp, battery->misc_event); mutex_unlock(&battery->misclock); if (battery->prev_misc_event != battery->misc_event) { @@ -979,8 +972,8 @@ static bool sec_bat_get_cable_type( return ret; } -static void sec_bat_set_charging_status(struct sec_battery_info *battery, - int status) { +void sec_bat_set_charging_status(struct sec_battery_info *battery, int status) +{ union power_supply_propval value; switch (status) { case POWER_SUPPLY_STATUS_CHARGING: @@ -1722,6 +1715,38 @@ static void sec_bat_aging_check(struct sec_battery_info *battery) } #endif +static void sec_bat_check_battery_health(struct sec_battery_info *battery) +{ + union power_supply_propval value; + battery_health_condition state; + int i, battery_health; + + /* check to support ASoC and Cycle */ + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_ENERGY_FULL, value); +#if !defined(CONFIG_BATTERY_AGE_FORECAST) + value.intval = -1; +#endif + if (value.intval <= 0 || battery->pdata->health_condition == NULL) { + pr_err("%s: does not support cycle or asoc or health_condition\n", __func__); + return; + } + /* Checking Cycle and ASoC */ + state.cycle = state.asoc = BATTERY_HEALTH_BAD; + for (i = BATTERY_HEALTH_MAX - 1; i >= 0; i--) { + if (battery->pdata->health_condition[i].cycle >= (battery->batt_cycle % 10000)) + state.cycle = i + BATTERY_HEALTH_GOOD; + if (battery->pdata->health_condition[i].asoc <= battery->batt_asoc) + state.asoc = i + BATTERY_HEALTH_GOOD; + } + battery_health = max(state.cycle, state.asoc); + pr_info("%s: update battery_health(%d), (%d - %d)\n", + __func__, battery_health, state.cycle, state.asoc); + /* Update battery health */ + sec_bat_set_misc_event(battery, + (battery_health << BATTERY_HEALTH_SHIFT), BATT_MISC_EVENT_BATTERY_HEALTH); +} + static bool sec_bat_temperature_check( struct sec_battery_info *battery) { @@ -2712,6 +2737,9 @@ static void sec_bat_do_fullcharged( #if defined(CONFIG_BATTERY_AGE_FORECAST) sec_bat_aging_check(battery); #endif +#if defined(CONFIG_PREVENT_SWELLING_BATTERY) + sec_bat_start_psb(battery); +#endif value.intval = POWER_SUPPLY_STATUS_FULL; psy_do_property(battery->pdata->fuelgauge_name, set, @@ -3800,6 +3828,11 @@ static void sec_bat_monitor_work( if (!battery->charging_block && battery->status != POWER_SUPPLY_STATUS_DISCHARGING) sec_bat_calculate_safety_time(battery); +#if defined(CONFIG_PREVENT_SWELLING_BATTERY) + sec_bat_update_psb_level(battery); + sec_bat_check_psb(battery); +#endif + dev_info(battery->dev, "%s: Status(%s), mode(%s), Health(%s), Cable(%d), level(%d%%), slate_mode(%d)" #if defined(CONFIG_AFC_CHARGER_MODE) @@ -4113,11 +4146,14 @@ static void sec_bat_cable_work(struct work_struct *work) if (battery->cable_type == POWER_SUPPLY_TYPE_OTG || battery->cable_type == POWER_SUPPLY_TYPE_POWER_SHARING) { - if (sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF)) - goto end_of_cable_work; + sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF); + goto end_of_cable_work; } else if (!keep_charging_state) { - if (sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING)) - goto end_of_cable_work; +#if defined(CONFIG_PREVENT_SWELLING_BATTERY) + sec_bat_check_full_state(battery); +#else + sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING); +#endif } #if defined(CONFIG_CALC_TIME_TO_FULL) @@ -5130,6 +5166,15 @@ ssize_t sec_bat_store_attrs( break; case FG_CAPACITY: break; + case FG_ASOC: + if (sscanf(buf, "%d\n", &x) == 1) { + if (x >= 0 && x <= 100) { + battery->batt_asoc = x; + sec_bat_check_battery_health(battery); + } + ret = count; + } + break; case AUTH: break; case CHG_CURRENT_ADC: @@ -5215,14 +5260,24 @@ ssize_t sec_bat_store_attrs( break; case STORE_MODE: if (sscanf(buf, "%d\n", &x) == 1) { - battery->store_mode = x ? true : false; - ret = count; - if (battery->store_mode) { - union power_supply_propval value; - value.intval = battery->store_mode; - psy_do_property(battery->pdata->charger_name, set, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, value); +#if !defined(CONFIG_SEC_FACTORY) + if (x) { + if (!battery->store_mode) { + battery->pdata->wpc_high_temp -= 30; + battery->pdata->wpc_high_temp_recovery -= 30; + } + battery->store_mode |= STORE_MODE_LDU_RDU; + if(battery->capacity <= 5) { + battery->ignore_store_mode = true; + } else { + if(battery->cable_type == POWER_SUPPLY_TYPE_HV_MAINS || \ + battery->cable_type == POWER_SUPPLY_TYPE_HV_MAINS_12V || + battery->cable_type == POWER_SUPPLY_TYPE_HV_ERR) + sec_bat_set_charging_current(battery); + } } +#endif + ret = count; } break; case UPDATE: @@ -5541,6 +5596,7 @@ ssize_t sec_bat_store_attrs( if (prev_battery_cycle < 0) { sec_bat_aging_check(battery); } + sec_bat_check_battery_health(battery); } ret = count; } @@ -6169,7 +6225,13 @@ static int sec_bat_get_property(struct power_supply *psy, return 0; } } - +#if defined(CONFIG_STORE_MODE) + if (battery->store_mode && !lpcharge && + battery->cable_type != POWER_SUPPLY_TYPE_BATTERY && + battery->status == POWER_SUPPLY_STATUS_DISCHARGING) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + } else +#endif val->intval = battery->status; } break; @@ -6743,7 +6805,9 @@ static int batt_handle_notification(struct notifier_block *nb, #endif block_water_event &= (battery->muic_cable_type != ATTACHED_DEV_UNDEFINED_CHARGING_MUIC) && (battery->muic_cable_type != ATTACHED_DEV_UNDEFINED_RANGE_MUIC); - sec_bat_set_misc_event(battery, BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE, block_water_event); + sec_bat_set_misc_event(battery, + (block_water_event ? 0 : BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE), + BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE); #ifdef CONFIG_CCIC_NOTIFIER /* If PD cable is already attached, return this function */ @@ -7908,16 +7972,17 @@ static int sec_bat_parse_dt(struct device *dev, kfree(battery->pdata->age_data); battery->pdata->age_data = NULL; battery->pdata->num_age_step = 0; - } - pr_err("%s num_age_step : %d\n", __func__, battery->pdata->num_age_step); - for (len = 0; len < battery->pdata->num_age_step; ++len) { - pr_err("[%d/%d]cycle:%d, float:%d, full_v:%d, recharge_v:%d, soc:%d\n", - len, battery->pdata->num_age_step-1, - battery->pdata->age_data[len].cycle, - battery->pdata->age_data[len].float_voltage, - battery->pdata->age_data[len].full_condition_vcell, - battery->pdata->age_data[len].recharge_condition_vcell, - battery->pdata->age_data[len].full_condition_soc); + } else { + pr_err("%s num_age_step : %d\n", __func__, battery->pdata->num_age_step); + for (len = 0; len < battery->pdata->num_age_step; ++len) { + pr_err("[%d/%d]cycle:%d, float:%d, full_v:%d, recharge_v:%d, soc:%d\n", + len, battery->pdata->num_age_step-1, + battery->pdata->age_data[len].cycle, + battery->pdata->age_data[len].float_voltage, + battery->pdata->age_data[len].full_condition_vcell, + battery->pdata->age_data[len].recharge_condition_vcell, + battery->pdata->age_data[len].full_condition_soc); + } } } else { battery->pdata->num_age_step = 0; @@ -7925,6 +7990,29 @@ static int sec_bat_parse_dt(struct device *dev, } #endif + p = of_get_property(np, "battery,health_condition", &len); + if (p || (len / sizeof(battery_health_condition)) == BATTERY_HEALTH_MAX) { + battery->pdata->health_condition = kzalloc(len, GFP_KERNEL); + ret = of_property_read_u32_array(np, "battery,health_condition", + (u32 *)battery->pdata->health_condition, len/sizeof(u32)); + if (ret) { + pr_err("%s failed to read battery->pdata->health_condition: %d\n", + __func__, ret); + kfree(battery->pdata->health_condition); + battery->pdata->health_condition = NULL; + } else { + for (i = 0; i < BATTERY_HEALTH_MAX; i++) { + pr_err("%s: [BATTERY_HEALTH] %d: Cycle(~ %d), ASoC(~ %d)\n", + __func__, i, + battery->pdata->health_condition[i].cycle, + battery->pdata->health_condition[i].asoc); + } + } + } else { + battery->pdata->health_condition = NULL; + pr_err("%s there is not health_condition, len(%d)\n", __func__, len); + } + ret = of_property_read_u32(np, "battery,siop_event_check_type", &pdata->siop_event_check_type); ret = of_property_read_u32(np, "battery,siop_call_cc_current", @@ -8166,14 +8254,8 @@ static int sec_battery_probe(struct platform_device *pdev) battery->cable_type = POWER_SUPPLY_TYPE_BATTERY; battery->test_mode = 0; battery->factory_mode = false; -#if defined(CONFIG_STORE_MODE) - battery->store_mode = false; - value.intval = battery->store_mode; - psy_do_property(battery->pdata->charger_name, set, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, value); -#else battery->store_mode = STORE_MODE_NONE; -#endif + battery->ignore_store_mode = false; battery->slate_mode = false; battery->is_hc_usb = false; @@ -8187,6 +8269,7 @@ static int sec_battery_probe(struct platform_device *pdev) battery->batt_cycle = -1; battery->pdata->age_step = 0; #endif + battery->batt_asoc = 100; battery->wpc_temp_mode = false; battery->health_change = false; @@ -8346,6 +8429,10 @@ static int sec_battery_probe(struct platform_device *pdev) goto err_req_irq; } +#if defined(CONFIG_PREVENT_SWELLING_BATTERY) + sec_bat_init_psb(battery); +#endif + value.intval = POWER_SUPPLY_TYPE_MAINS; psy_do_property(battery->pdata->charger_name, get, POWER_SUPPLY_PROP_CURRENT_AVG, value); @@ -8375,6 +8462,15 @@ static int sec_battery_probe(struct platform_device *pdev) psy_do_property(battery->pdata->wireless_charger_name, set, POWER_SUPPLY_PROP_CHARGE_TYPE, value); +#if defined(CONFIG_STORE_MODE) && !defined(CONFIG_SEC_FACTORY) + battery->store_mode |= STORE_MODE_LDU_RDU; + if (battery->capacity <= 5) + battery->ignore_store_mode = true; + + battery->pdata->wpc_high_temp -= 30; + battery->pdata->wpc_high_temp_recovery -= 30; +#endif + #if defined(CONFIG_MUIC_NOTIFIER) muic_notifier_register(&battery->batt_nb, batt_handle_notification, diff --git a/drivers/battery/sec_psb.c b/drivers/battery/sec_psb.c new file mode 100644 index 000000000000..11e524ac9a12 --- /dev/null +++ b/drivers/battery/sec_psb.c @@ -0,0 +1,764 @@ +/* + * sec_psb.c + * Samsung Mobile Battery Driver + * + * Copyright (C) 2019 Samsung Electronics + * + * + * 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 + * published by the Free Software Foundation. + */ +#include + +#define DEFAULT_TRIGGER_TIME_AFTER_FULL 48 * 60 * 60 +#define DEFAULT_RELEASE_DISCHARGING_TIME 2 * 60 * 60 +#define DEFAULT_MAX_CYCLE 1000 + +#define CMD_MAX 0 +#define CMD_MIN 1 +#define CMD_ADD 2 +#define CMD_CLEAR 3 + +typedef struct { + unsigned int trigger_time_after_full; + unsigned int release_discharging_time; + unsigned int release_discharging_soc; + unsigned int release_discharging_voltage; + unsigned int max_cycle; + unsigned int num_age_step; + unsigned int *step_count_condition; +} sec_psb_pdata; + +typedef struct { + sec_psb_pdata *pdata; + + unsigned long start_charging_time; + unsigned int prev_charging_time; + bool is_time_set; + + int raw_soc; + + unsigned int state; + unsigned long start_time_after_full; + unsigned long start_time_after_discharging; + int adjust_cycle; + int prevent_adjust_cycle; + int adjust_age_step; + unsigned int *step_count; + + unsigned int *data; +} sec_psb; + +enum sec_psb_sysfs { + PSB_DATA = 0, +#if defined(CONFIG_BATTERY_CISD) + PSB_DATA_JSON, +#endif + PSB_CONDITION, +}; + +enum sec_psb_state { + PSB_STATE_NONE = 0, + PSB_STATE_CHARGE_DONE, + PSB_STATE_DISCHARGING, + PSB_STATE_CHARGING, +}; + +enum sec_psb_type { + PSB_TYPE_NONE = 0, /* TEMP */ + PSB_TYPE_MAX_CHARGING_TIME, + PSB_TYPE_TEMP_CHARGING_TIME, + PSB_TYPE_LEVEL, + PSB_TYPE_MAX_CYCLE, + PSB_TYPE_STEP_COUNT, +}; + +#if defined(CONFIG_BATTERY_CISD) +const char *psb_data_json_str[] = {NULL, "MAX_CHARGING_TIME", NULL, "LEVEL", "MAX_CYCLE", "STEP_COUNT"}; +#endif + +static ssize_t sec_psb_show_attrs(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t sec_psb_store_attrs(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +#define SEC_PSB_ATTR(_name) \ +{ \ + .attr = {.name = #_name, .mode = 0664}, \ + .show = sec_psb_show_attrs, \ + .store = sec_psb_store_attrs, \ +} + +static struct device_attribute sec_psb_attrs[] = { + SEC_PSB_ATTR(psb_data), +#if defined(CONFIG_BATTERY_CISD) + SEC_PSB_ATTR(psb_data_json), +#endif + SEC_PSB_ATTR(psb_condition), +}; + +static void sec_psb_set_data(sec_psb *psb, int index, int cmd, unsigned int value) +{ + switch (cmd) { + case CMD_MAX: + psb->data[index] = max(psb->data[index], value); + break; + case CMD_MIN: + psb->data[index] = min(psb->data[index], value); + break; + case CMD_ADD: + psb->data[index] += value; + break; + default: + break; + } +} + +static bool sec_psb_check_step_count(struct sec_battery_info *battery, int step) +{ + static bool is_skip = false; + sec_psb *psb = battery->psb; + + if (is_skip || step == 0) + pr_info("%s: skip function\n", __func__); + else if (psb->pdata->step_count_condition[step] && + psb->data[PSB_TYPE_STEP_COUNT + step] >= psb->pdata->step_count_condition[step]) + is_skip = true; + + return is_skip; +} + +static bool sec_psb_update_age_data(struct sec_battery_info *battery, int cycle) +{ +#if defined(CONFIG_BATTERY_AGE_FORECAST) + sec_psb *psb = battery->psb; + int prev_step = psb->adjust_age_step; + int calc_step = -1; + + for (calc_step = battery->pdata->num_age_step - 1; calc_step >= 0; calc_step--) { + if (battery->pdata->age_data[calc_step].cycle <= cycle) + break; + } + + if (calc_step == prev_step) + return false; + + psb->adjust_age_step = calc_step; + sec_psb_set_data(psb, PSB_TYPE_STEP_COUNT + calc_step, CMD_ADD, 1); + + if (!sec_psb_check_step_count(battery, calc_step)) + return false; + + /* float voltage */ + battery->pdata->chg_float_voltage = + battery->pdata->age_data[calc_step].float_voltage; + battery->pdata->swelling_normal_float_voltage = + battery->pdata->chg_float_voltage; + /* full/recharge condition */ + battery->pdata->recharge_condition_vcell = + battery->pdata->age_data[calc_step].recharge_condition_vcell; + battery->pdata->full_condition_soc = + battery->pdata->age_data[calc_step].full_condition_soc; + battery->pdata->full_condition_vcell = + battery->pdata->age_data[calc_step].full_condition_vcell; + return true; +#else + return false; +#endif +} + +void sec_bat_update_psb_level(struct sec_battery_info *battery) +{ + sec_psb *psb = battery->psb; + union power_supply_propval value = {0, }; + + + if (battery->status == POWER_SUPPLY_STATUS_FULL && + battery->capacity >= 100) { + + value.intval = SEC_FUELGAUGE_CAPACITY_TYPE_RAW; + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_CAPACITY, value); + + if (psb->raw_soc < value.intval) { + psb->raw_soc = value.intval; + pr_info("%s: [%s] update raw_soc(%d)\n", + __func__, sec_bat_status_str[battery->status], psb->raw_soc); + } else if (psb->raw_soc > value.intval) { + sec_psb_set_data(psb, PSB_TYPE_LEVEL, CMD_ADD, (psb->raw_soc - value.intval)); + +#if defined(CONFIG_BATTERY_AGE_FORECAST) + //battery->batt_cycle += adj_cycle; +#endif + pr_info("%s: [%s] bc(%d), rs(%d), nrs(%d), pl(%d)\n", + __func__, sec_bat_status_str[battery->status], + battery->batt_cycle, psb->raw_soc, value.intval, psb->data[PSB_TYPE_LEVEL]); + + psb->raw_soc = value.intval; + } + } else + psb->raw_soc = 0; +} + +void sec_bat_start_psb(struct sec_battery_info *battery) +{ + sec_psb *psb = battery->psb; + struct timespec ts = {0, }; + int batt_cycle = 0; + +#if defined(CONFIG_BATTERY_AGE_FORECAST) + batt_cycle = battery->batt_cycle; +#endif + if (batt_cycle > psb->pdata->max_cycle) + return; + else if ((battery->status == POWER_SUPPLY_STATUS_FULL) && + (psb->state == PSB_STATE_CHARGE_DONE)) { + psb->start_time_after_discharging = 0; + return; + } + psb->state = PSB_STATE_CHARGE_DONE; + psb->start_time_after_discharging = 0; + get_monotonic_boottime(&ts); + psb->start_time_after_full = ts.tv_sec; + pr_info("%s: [2ND-FULL] staf(%ld)\n", __func__, psb->start_time_after_full); +} + +static void sec_psb_check_float_voltage(struct sec_battery_info *battery) +{ + bool is_swelling_mode = false; + +#if defined(CONFIG_BATTERY_SWELLING) + is_swelling_mode = !!battery->swelling_mode; +#endif + + if (!is_swelling_mode) { + union power_supply_propval value = {0, }; + bool is_charging = !battery->charging_block; + + /* check float voltage */ + psy_do_property(battery->pdata->charger_name, get, + POWER_SUPPLY_PROP_VOLTAGE_MAX, value); + if (is_charging) + sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF); + if (value.intval != battery->pdata->chg_float_voltage) { + value.intval = battery->pdata->chg_float_voltage; + psy_do_property(battery->pdata->charger_name, set, + POWER_SUPPLY_PROP_VOLTAGE_MAX, value); + } + if (is_charging) + sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING); + } +} + +void sec_bat_check_psb(struct sec_battery_info *battery) +{ + sec_psb *psb = battery->psb; + struct timespec ts = {0, }; + unsigned long diff_time; + bool is_age_step_changed; + int batt_cycle = 0; + +#if defined(CONFIG_BATTERY_AGE_FORECAST) + batt_cycle = battery->batt_cycle; +#endif + if (batt_cycle > psb->pdata->max_cycle) + return; + else if (psb->state == PSB_STATE_NONE) + return; + + switch (battery->status) { + case POWER_SUPPLY_STATUS_CHARGING: + /* + * case1. full --> charging + */ + if (psb->state == PSB_STATE_CHARGE_DONE) { + /* update values about full state */ + psb->state = PSB_STATE_CHARGING; + psb->prevent_adjust_cycle += psb->adjust_cycle; + psb->adjust_cycle = 0; + psb->start_time_after_full = 0; + psb->start_time_after_discharging = 0; + pr_info("%s: [CHARGING] pac(%d), staf(%ld), stad(%ld)\n", + __func__, psb->prevent_adjust_cycle, + psb->start_time_after_full, + psb->start_time_after_discharging); + } + break; + case POWER_SUPPLY_STATUS_FULL: + /* + * case1. full + */ + if (psb->state == PSB_STATE_CHARGE_DONE) { + get_monotonic_boottime(&ts); + /* calculate idle time after charge done */ + diff_time = (ts.tv_sec >= psb->start_time_after_full) ? + (ts.tv_sec - psb->start_time_after_full) : + (0xFFFFFFFF - psb->start_time_after_full + ts.tv_sec); + psb->adjust_cycle = + (((diff_time * 100) * psb->pdata->max_cycle) / + psb->pdata->trigger_time_after_full) / 100; + pr_info("%s: [FULL] ac(%d), pac(%d), staf(%ld), dt(%ld)\n", + __func__, + psb->adjust_cycle, psb->prevent_adjust_cycle, + psb->start_time_after_full, diff_time); + + is_age_step_changed = sec_psb_update_age_data(battery, + psb->adjust_cycle + psb->prevent_adjust_cycle); + pr_info("%s: [FULL] icas(%d), vn(%d), rcv(%d)\n", + __func__, is_age_step_changed, + battery->voltage_now, battery->pdata->recharge_condition_vcell); + + if (is_age_step_changed) { + battery->is_recharging = false; + battery->charging_mode = SEC_BATTERY_CHARGING_NONE; + sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_BUCK_OFF); + } else if (battery->is_recharging) { + sec_psb_check_float_voltage(battery); + } + } + return; + default: /* DISCHARGING or NOT-CHARGING */ + /* + * case1. full --> discharging + * case2. full --> discharging --> charging --> discharging + * case3. full --> charging --> discharging + * case4. full --> charging --> full --> discharging + * ... + */ + get_monotonic_boottime(&ts); + + if (psb->state != PSB_STATE_DISCHARGING) { + /* update values about full state */ + psb->state = PSB_STATE_DISCHARGING; + psb->prevent_adjust_cycle += psb->adjust_cycle; + psb->adjust_cycle = 0; + psb->start_time_after_full = 0; + psb->start_time_after_discharging = ts.tv_sec; + pr_info("%s: [DISCHARGING] pac(%d), staf(%ld), stad(%ld)\n", + __func__, psb->prevent_adjust_cycle, + psb->start_time_after_full, + psb->start_time_after_discharging); + } else { + /* calculate discharging time */ + diff_time = (ts.tv_sec >= psb->start_time_after_discharging) ? + (ts.tv_sec - psb->start_time_after_discharging) : + (0xFFFFFFFF - psb->start_time_after_discharging + ts.tv_sec); + pr_info("%s: [DISCHARGING] dt(%ld)\n", __func__, diff_time); + + /* check release condition (discharging time) */ + if (psb->pdata->release_discharging_time < diff_time) { + /* release age step */ + psb->state = PSB_STATE_NONE; + psb->start_time_after_full = 0; + psb->start_time_after_discharging = 0; + psb->adjust_cycle = 0; + psb->prevent_adjust_cycle = 0; + //psb->adjust_age_step = 0; + sec_psb_update_age_data(battery, batt_cycle); + } + } + break; + } + + /* check release condition (voltage or soc) */ + if ((battery->voltage_now < psb->pdata->release_discharging_voltage) || + (battery->capacity < psb->pdata->release_discharging_soc)) { + /* release age step */ + psb->state = PSB_STATE_NONE; + psb->start_time_after_full = 0; + psb->start_time_after_discharging = 0; + psb->adjust_cycle = 0; + psb->prevent_adjust_cycle = 0; + //psb->adjust_age_step = 0; + sec_psb_update_age_data(battery, batt_cycle); + pr_info("%s: [%s] release state(voltage:%d, soc:%d)\n", + __func__, sec_bat_status_str[battery->status], battery->voltage_now, battery->capacity); + + /* update float voltage */ + sec_psb_check_float_voltage(battery); + } + + sec_psb_set_data(psb, PSB_TYPE_MAX_CYCLE, + CMD_MAX, psb->adjust_cycle + psb->prevent_adjust_cycle); +} + +void sec_bat_check_full_state(struct sec_battery_info *battery) +{ + union power_supply_propval value = {0, }; + sec_psb *psb = battery->psb; + + /* check float voltage & status */ + psy_do_property(battery->pdata->fuelgauge_name, get, + POWER_SUPPLY_PROP_VOLTAGE_NOW, value); + pr_info("%s: check full-state(%d, %d, %d, %d, %d)\n", __func__, + psb->state, psb->adjust_cycle, psb->prevent_adjust_cycle, + battery->capacity, value.intval); + if ((battery->status != POWER_SUPPLY_STATUS_FULL) && + (battery->capacity >= 100) && + (value.intval >= battery->pdata->chg_float_voltage)) { + sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_FULL); + battery->charging_mode = SEC_BATTERY_CHARGING_NONE; + battery->is_recharging = false; + sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_BUCK_OFF); + + /* start psb */ + sec_bat_start_psb(battery); + } else { + sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING); + } +} + +static void sec_psb_check_charging_time(struct sec_battery_info *battery) +{ + sec_psb *psb = battery->psb; + struct timespec ts = {0, }; + unsigned int total_charging_time = 0; + + if (!is_nocharge_type(battery->cable_type) && !psb->start_charging_time) { + get_monotonic_boottime(&ts); + psb->start_charging_time = ts.tv_sec; + psb->prev_charging_time = psb->data[PSB_TYPE_TEMP_CHARGING_TIME]; + } else if (psb->start_charging_time) { + get_monotonic_boottime(&ts); + total_charging_time = ts.tv_sec - psb->start_charging_time; + sec_psb_set_data(psb, PSB_TYPE_MAX_CHARGING_TIME, + CMD_MAX, total_charging_time + psb->prev_charging_time); + if (psb->is_time_set) + psb->data[PSB_TYPE_TEMP_CHARGING_TIME] = + total_charging_time + psb->prev_charging_time; + + if (is_nocharge_type(battery->cable_type)) { + if (psb->is_time_set) + psb->data[PSB_TYPE_TEMP_CHARGING_TIME] = 0; + psb->prev_charging_time = 0; + psb->start_charging_time = 0; + } + } +} + +static ssize_t sec_psb_show_attrs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct sec_battery_info *battery = + container_of(psy, struct sec_battery_info, psy_bat); + const ptrdiff_t offset = attr - sec_psb_attrs; + sec_psb *psb = battery->psb; + char temp_buf[1024] = {0,}; + int i = 0, j = 0, size = 1024; + + switch (offset) { + case PSB_DATA: + /* update charging time */ + sec_psb_check_charging_time(battery); + + for (j = 0; j < PSB_TYPE_STEP_COUNT + psb->pdata->num_age_step; j++) { + snprintf(temp_buf + strlen(temp_buf), size, "%d ", psb->data[j]); + size = sizeof(temp_buf) - strlen(temp_buf); + } + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", temp_buf); + break; +#if defined(CONFIG_BATTERY_CISD) + case PSB_DATA_JSON: + for (j = 0; j < PSB_TYPE_STEP_COUNT + psb->pdata->num_age_step; j++) { + if (j >= PSB_TYPE_STEP_COUNT) + sprintf(temp_buf+strlen(temp_buf), ",\"%s_%d\":\"%d\"", + psb_data_json_str[PSB_TYPE_STEP_COUNT], j - PSB_TYPE_STEP_COUNT, psb->data[j]); + else if (psb_data_json_str[j] != NULL) + sprintf(temp_buf+strlen(temp_buf), "\"%s\":\"%d\"", + psb_data_json_str[j], psb->data[j]); + } + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", temp_buf); + break; +#endif + case PSB_CONDITION: + i += scnprintf(buf + i, PAGE_SIZE - i, "tt:%d rt:%d rs:%d rv:%d, ac:%d, pc:%d\n", + psb->pdata->trigger_time_after_full, psb->pdata->release_discharging_time, + psb->pdata->release_discharging_soc, psb->pdata->release_discharging_voltage, + psb->adjust_cycle, psb->prevent_adjust_cycle); + break; + default: + i = -EINVAL; + break; + } + + return i; +} + +static ssize_t sec_psb_store_attrs(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct sec_battery_info *battery = + container_of(psy, struct sec_battery_info, psy_bat); + const ptrdiff_t offset = attr - sec_psb_attrs; + sec_psb *psb = battery->psb; + int ret = -EINVAL, x = 0; + + switch (offset) { + case PSB_DATA: + { + const char *p = buf; + int i = 0; + pr_info("%s: %s\n", __func__, buf); + + for (i = 0; i < PSB_TYPE_STEP_COUNT + psb->pdata->num_age_step; i++) { + if (sscanf(p, "%10d%n", &psb->data[i], &x) > 0) { + p += (size_t)x; + + /* update step condition */ + if (i >= PSB_TYPE_STEP_COUNT) + sec_psb_check_step_count(battery, i - PSB_TYPE_STEP_COUNT); + } else { + pr_info("%s: NO DATA (IDX:%d, PSB_STEP_COUNT)\n", __func__, i); + psb->data[i] = 0; + break; + } + } + + /* check clear cmd */ + if (psb->data[PSB_TYPE_NONE] == CMD_CLEAR) { + for (i = 0; i < PSB_TYPE_STEP_COUNT + psb->pdata->num_age_step; i++) + psb->data[i] = 0; + } else { + if (is_nocharge_type(battery->cable_type)) { + sec_psb_set_data(psb, PSB_TYPE_MAX_CHARGING_TIME, CMD_MAX, psb->data[PSB_TYPE_TEMP_CHARGING_TIME]); + psb->data[PSB_TYPE_TEMP_CHARGING_TIME] = 0; + } else { + psb->prev_charging_time = psb->data[PSB_TYPE_TEMP_CHARGING_TIME]; + } + psb->is_time_set = true; + } + ret = count; + } + break; +#if defined(CONFIG_BATTERY_CISD) + case PSB_DATA_JSON: + ret = count; + break; +#endif + case PSB_CONDITION: + { + char tc; + + if (sscanf(buf, "%c %d\n", &tc, &x) == 2) { + unsigned int old_value = 0; + + switch (tc) { + case 't': + old_value = psb->pdata->trigger_time_after_full; + psb->pdata->trigger_time_after_full = x; + break; + case 'r': + old_value = psb->pdata->release_discharging_time; + psb->pdata->release_discharging_time = x; + break; + case 's': + old_value = psb->pdata->release_discharging_soc; + psb->pdata->release_discharging_soc = x; + break; + case 'v': + old_value = psb->pdata->release_discharging_voltage; + psb->pdata->release_discharging_voltage = x; + break; + case 'c': + old_value = psb->prevent_adjust_cycle; + if (psb->state != PSB_STATE_NONE) { + psb->state = PSB_STATE_NONE; + psb->start_time_after_full = 0; + psb->start_time_after_discharging = 0; + psb->adjust_cycle = 0; + psb->prevent_adjust_cycle = 0; + //psb->adjust_age_step = 0; + sec_psb_update_age_data(battery, battery->batt_cycle); + pr_info("%s: init psb parameters.\n", __func__); + } + psb->prevent_adjust_cycle = x; + break; + default: + break; + } + pr_info("%s: changed condition(%c: %d --> %d)\n", + __func__, tc, old_value, x); + ret = count; + } + } + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +void sec_bat_init_psb(struct sec_battery_info *battery) +{ + struct device_node *np = NULL; + sec_psb *psb = NULL; + sec_psb_pdata *pdata = NULL; + const u32 *p = NULL; + int ret = 0, len = 0, i; + char temp_buf[1024] = {0,}; + + /* 0. create & alloc psb */ + psb = kzalloc(sizeof(sec_psb), GFP_KERNEL); + if (!psb) { + pr_err("%s: failed to alloc sec_psd\n", __func__); + return; + } + pdata = kzalloc(sizeof(sec_psb_pdata), GFP_KERNEL); + if (!pdata) { + pr_err("%s: failed to alloc pdata\n", __func__); + goto err_alloc_pdata; + } + psb->pdata = pdata; +#if defined(CONFIG_BATTERY_AGE_FORECAST) + pdata->num_age_step = battery->pdata->num_age_step; +#endif + if (pdata->num_age_step > 0) { + pdata->step_count_condition = + kzalloc(sizeof(unsigned int) * pdata->num_age_step, GFP_KERNEL); + if (!pdata->step_count_condition) { + pr_err("%s: failed to alloc step array\n", __func__); + goto err_alloc_step_condition; + } + } + + len = (PSB_TYPE_STEP_COUNT + pdata->num_age_step) * sizeof(unsigned int); + psb->data = kzalloc(len, GFP_KERNEL); + if (!psb->data) { + pr_err("%s: failed to alloc data\n", __func__); + goto err_alloc_data; + } + + /* 1. update condition values from the dt data. */ + np = of_find_node_by_name(NULL, "prevent_swelling_battery"); + if (!np) { + pr_err("%s: failed to find battery node\n", __func__); + goto err_find_node; + } + ret = of_property_read_u32(np, "psb,trigger_time_after_full", + &pdata->trigger_time_after_full); + if (ret) + pdata->trigger_time_after_full = DEFAULT_TRIGGER_TIME_AFTER_FULL; + + ret = of_property_read_u32(np, "psb,release_discharging_time", + &pdata->release_discharging_time); + if (ret) + pdata->release_discharging_time = DEFAULT_RELEASE_DISCHARGING_TIME; + + ret = of_property_read_u32(np, "psb,release_discharging_soc", + &pdata->release_discharging_soc); + if (ret) { +#if defined(CONFIG_BATTERY_AGE_FORECAST) + pdata->release_discharging_soc = 100 - + battery->pdata->age_data[pdata->num_age_step - 1].full_condition_soc; +#else + pdata->release_discharging_soc = battery->pdata->full_condition_soc; +#endif + } + + ret = of_property_read_u32(np, "psb,release_discharging_voltage", + &pdata->release_discharging_voltage); + if (ret) { +#if defined(CONFIG_BATTERY_AGE_FORECAST) + pdata->release_discharging_voltage = + battery->pdata->age_data[pdata->num_age_step - 1].full_condition_vcell; +#else + pdata->release_discharging_voltage = battery->pdata->full_condition_soc; +#endif + } + + if (pdata->num_age_step > 0) { + p = of_get_property(np, "psb,step_count_condition", &len); + if (p) { + /* check array size */ + len = len / sizeof(unsigned int); + if (len > pdata->num_age_step) + len = pdata->num_age_step; + + ret = of_property_read_u32_array(np, "psb,step_count_condition", + (u32 *)pdata->step_count_condition, len); + if (ret) { + pr_err("%s: failed to read step_count_condition(ret:%d - len:%d)\n", + __func__, ret, len); + + for (i = 0; i < len; i++) + pdata->step_count_condition[i] = 0; + } + } + for (i = 0; i < pdata->num_age_step; i++) + sprintf(temp_buf+strlen(temp_buf), "%d ", pdata->step_count_condition[i]); + } else + sprintf(temp_buf, "NULL"); + +#if defined(CONFIG_BATTERY_AGE_FORECAST) + pdata->max_cycle = battery->pdata->age_data[battery->pdata->num_age_step - 1].cycle; +#else + pdata->max_cycle = DEFAULT_MAX_CYCLE; +#endif + pr_err("%s: read dt(tt:%d, rt:%d, rs:%d, rv:%d, mc:%d, scc:%s)\n", __func__, + pdata->trigger_time_after_full, + pdata->release_discharging_time, + pdata->release_discharging_soc, + pdata->release_discharging_voltage, + pdata->max_cycle, + temp_buf); + + /* 2. create sysfs nodes. */ + for (i = 0; i < ARRAY_SIZE(sec_psb_attrs); i++) { + ret = device_create_file(battery->psy_bat.dev, &sec_psb_attrs[i]); + if (ret) + goto err_create_attr; + } + + /* 3. init state values. */ + psb->raw_soc = 0; + + psb->state = PSB_STATE_NONE; + psb->start_time_after_full = 0; + psb->start_time_after_discharging = 0; + psb->adjust_cycle = 0; + psb->prevent_adjust_cycle = 0; + psb->adjust_age_step = 0; + + battery->psb = psb; + return; + +err_create_attr: + while (i--) + device_remove_file(battery->dev, &sec_psb_attrs[i]); +err_find_node: + if (psb->data) + kfree(psb->data); +err_alloc_data: + if (pdata->step_count_condition) + kfree(pdata->step_count_condition); +err_alloc_step_condition: + kfree(psb->pdata); +err_alloc_pdata: + kfree(psb); +} + +void sec_bat_remove_psb(struct sec_battery_info *battery) +{ + sec_psb *psb = battery->psb; + + if (!psb) + return; + + if (psb->pdata) { + sec_psb_pdata *pdata = psb->pdata; + + if (pdata->step_count_condition) + kfree(pdata->step_count_condition); + kfree(psb->pdata); + } + if (psb->data) + kfree(psb->data); + kfree(psb); +} + diff --git a/include/linux/battery/sec_battery.h b/include/linux/battery/sec_battery.h index a046019457c2..034d0423e0a5 100755 --- a/include/linux/battery/sec_battery.h +++ b/include/linux/battery/sec_battery.h @@ -62,8 +62,8 @@ #define STORE_MODE_CHARGING_MAX 35 #define STORE_MODE_CHARGING_MIN 30 #else -//#define STORE_MODE_CHARGING_MAX 70 -//#define STORE_MODE_CHARGING_MIN 60 +#define STORE_MODE_CHARGING_MAX 70 +#define STORE_MODE_CHARGING_MIN 60 #endif #define ADC_CH_COUNT 10 @@ -82,6 +82,19 @@ #define SIOP_HV_CHARGING_LIMIT_CURRENT 1000 #define BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE 0x00000001 +#define BATT_MISC_EVENT_BATTERY_HEALTH 0x000F0000 + +#define BATTERY_HEALTH_SHIFT 16 +enum misc_battery_health { + BATTERY_HEALTH_UNKNOWN = 0, + BATTERY_HEALTH_GOOD, + BATTERY_HEALTH_NORMAL, + BATTERY_HEALTH_AGED, + BATTERY_HEALTH_MAX = BATTERY_HEALTH_AGED, + + /* For event */ + BATTERY_HEALTH_BAD = 0xF, +}; enum store_mode_type { STORE_MODE_NONE = 0, @@ -173,7 +186,11 @@ struct sec_battery_info { #if defined(CONFIG_BATTERY_CISD) struct cisd cisd; -#endif +#endif + +#if defined(CONFIG_PREVENT_SWELLING_BATTERY) + void *psb; +#endif /* battery check */ unsigned int check_count; @@ -334,6 +351,7 @@ struct sec_battery_info { #if defined(CONFIG_BATTERY_AGE_FORECAST) int batt_cycle; #endif + int batt_asoc; #if defined(CONFIG_STEP_CHARGING) unsigned int step_charging_type; int step_charging_status; diff --git a/include/linux/battery/sec_charging_common.h b/include/linux/battery/sec_charging_common.h index 312087e505c7..eaae751bd96a 100755 --- a/include/linux/battery/sec_charging_common.h +++ b/include/linux/battery/sec_charging_common.h @@ -506,6 +506,11 @@ struct sec_age_data { struct sec_age_data #endif +typedef struct { + unsigned int cycle; + unsigned int asoc; +} battery_health_condition; + struct sec_battery_platform_data { /* NO NEED TO BE CHANGED */ /* callback functions */ @@ -796,6 +801,9 @@ struct sec_battery_platform_data { int age_data_length; sec_age_data_t* age_data; #endif + + battery_health_condition* health_condition; + unsigned int siop_event_check_type; unsigned int siop_call_cc_current; unsigned int siop_call_cv_current; @@ -925,6 +933,11 @@ static inline struct power_supply *get_power_supply_by_name(char *name) } \ } +#define is_nocharge_type(cable_type) ( \ + cable_type == POWER_SUPPLY_TYPE_BATTERY || \ + cable_type == POWER_SUPPLY_TYPE_OTG || \ + cable_type == POWER_SUPPLY_TYPE_POWER_SHARING) + #ifndef CONFIG_OF #define adc_init(pdev, pdata, channel) \ (((pdata)->adc_api)[((((pdata)->adc_type[(channel)]) < \ diff --git a/include/linux/battery/sec_psb.h b/include/linux/battery/sec_psb.h new file mode 100644 index 000000000000..5acce26afe84 --- /dev/null +++ b/include/linux/battery/sec_psb.h @@ -0,0 +1,36 @@ +/* + * sec_psb.h + * Samsung Mobile 'Prevent Swelling Battery' Header + * + * Copyright (C) 2019 Samsung Electronics, Inc. + * + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __SEC_PSB_H +#define __SEC_PSB_H __FILE__ + +#include + +extern char *sec_bat_status_str[]; + +int sec_bat_set_charge(struct sec_battery_info *battery, int chg_mode); +void sec_bat_set_charging_status(struct sec_battery_info *battery, int status); + +void sec_bat_update_psb_level(struct sec_battery_info *battery); +void sec_bat_start_psb(struct sec_battery_info *battery); +void sec_bat_check_psb(struct sec_battery_info *battery); +void sec_bat_check_full_state(struct sec_battery_info *battery); + +void sec_bat_init_psb(struct sec_battery_info *battery); +void sec_bat_remove_psb(struct sec_battery_info *battery); + +#endif /* __SEC_PSB_H */