/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2021 MediaTek Inc. */ /***************************************************************************** * * Filename: * --------- * mtk_battery.c * * Project: * -------- * Android_Software * * Description: * ------------ * This Module defines functions of the Anroid Battery service for * updating the battery status * * Author: * ------- * Weiching Lin * ****************************************************************************/ #include /* For init/exit macros */ #include /* For MODULE_ marcros */ #include /* For wait queue*/ #include /* For wait queue*/ #include /* For Kthread_run */ #include /* platform device */ #include #include /* netlink */ #include #include /* netlink */ #include /* netlink */ #include /* netlink */ #include /* cdev */ #include /* IS_ERR, PTR_ERR */ #include /*kernel_power_off*/ #include #include /*of_dt API*/ #include /*of_find_node_by_name*/ #include #include /*irq_to_desc*/ #include /*mt6358_irq_get_virq*/ #include #include /*div_s64*/ #include #include #include #include #include #include #include "mtk_battery_internal.h" #include #include #include #include #ifdef CONFIG_BATTERY_SAMSUNG #include <../drivers/battery/common/sec_charging_common.h> #define SEC_BATTERY_FAKE_CAPACITY 0 #endif /* ============================================================ */ /* define */ /* ============================================================ */ #define NETLINK_FGD 26 /************ adc_cali *******************/ #define ADC_CALI_DEVNAME "MT_pmic_adc_cali" #define TEST_ADC_CALI_PRINT _IO('k', 0) #define SET_ADC_CALI_Slop _IOW('k', 1, int) #define SET_ADC_CALI_Offset _IOW('k', 2, int) #define SET_ADC_CALI_Cal _IOW('k', 3, int) #define ADC_CHANNEL_READ _IOW('k', 4, int) #define BAT_STATUS_READ _IOW('k', 5, int) #define Set_Charger_Current _IOW('k', 6, int) /* add for meta tool----------------------------------------- */ #define Get_META_BAT_VOL _IOW('k', 10, int) #define Get_META_BAT_SOC _IOW('k', 11, int) #define Get_META_BAT_CAR_TUNE_VALUE _IOW('k', 12, int) #define Set_META_BAT_CAR_TUNE_VALUE _IOW('k', 13, int) #define Set_BAT_DISABLE_NAFG _IOW('k', 14, int) #define Set_CARTUNE_TO_KERNEL _IOW('k', 15, int) /* add for meta tool----------------------------------------- */ static struct class *adc_cali_class; static int adc_cali_major; static dev_t adc_cali_devno; static struct cdev *adc_cali_cdev; #ifdef CONFIG_BATTERY_SAMSUNG static void disable_fg(void); #endif static int adc_cali_slop[14] = { 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000 }; static int adc_cali_offset[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static int adc_cali_cal[1] = { 0 }; static int battery_in_data[1] = { 0 }; static int battery_out_data[1] = { 0 }; static bool g_ADC_Cali; static enum power_supply_property battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, }; /* boot mode */ struct tag_bootmode { u32 size; u32 tag; u32 bootmode; u32 boottype; }; /* weak function */ int __attribute__ ((weak)) do_ptim_gauge( bool isSuspend, unsigned int *bat, signed int *cur, bool *is_charging) { return 0; } int __attribute__ ((weak)) get_rac(void) { return 0; } int __attribute__ ((weak)) get_imix(void) { return 0; } void __attribute__ ((weak)) battery_dump_nag(void) { } enum charger_type __attribute__ ((weak)) mt_get_charger_type(void) { return CHARGER_UNKNOWN; } int __attribute__ ((weak)) charger_manager_get_zcv( struct charger_consumer *consumer, int idx, u32 *uV) { return 0; } struct charger_consumer __attribute__ ((weak)) *charger_manager_get_by_name(struct device *dev, const char *supply_name) { return NULL; } int __attribute__ ((weak)) register_charger_manager_notifier(struct charger_consumer *consumer, struct notifier_block *nb) { return 0; } void __attribute__ ((weak)) enable_bat_temp_det(bool en) { pr_notice("[%s] not support!\n", __func__); } unsigned int __attribute__ ((weak)) mt6358_irq_get_virq(struct device *dev, unsigned int hwirq) { pr_notice_once("%s: API not ready!\n", __func__); return 0; } /* weak function end */ int gauge_enable_interrupt(int intr_number, int en) { struct irq_desc *desc; unsigned int depth = 0; unsigned int irq = 0; if (intr_number == INT_ENUM_MAX) { bm_debug("[%s] intr_number = INT_ENUM_MAX, no interrupt, do nothing %d\n", __func__, INT_ENUM_MAX); return 0; } if (gm.pdev_node == NULL) gm.pdev_node = of_find_node_by_name(NULL, "mt-pmic"); if (gm.pdevice == NULL) gm.pdevice = of_find_device_by_node(gm.pdev_node); if (gm.pmic_dev == NULL && gm.pdevice != NULL) gm.pmic_dev = &gm.pdevice->dev; if (gm.pmic_dev == NULL) return -EINVAL; irq = mt6358_irq_get_virq(gm.pmic_dev->parent, intr_number); desc = irq_to_desc(irq); if (!desc) return -EINVAL; mutex_lock(&gm.pmic_intr_mutex); depth = desc->depth; if (en == 1) { if (desc->depth == 1) { pmic_enable_interrupt(intr_number, en, "GM30"); bm_debug("[%s]intr_number:%d %d en = %d, depth b[%d],a[%d]\n", __func__, intr_number, irq, en, depth, desc->depth); } else { /* do nothing */ bm_debug("[%s]do nothing, intr_number:%d %d en = %d, depth:%d %d\n", __func__, intr_number, irq, en, depth, desc->depth); } } else if (en == 0) { if (desc->depth == 0) { pmic_enable_interrupt(intr_number, en, "GM30"); bm_debug("[%s]intr_number:%d %d en = %d, depth b[%d],a[%d]\n", __func__, intr_number, irq, en, depth, desc->depth); } else { /* do nothing */ bm_debug("[%s]do nothing, intr_number:%d %d en = %d, depth:%d %d\n", __func__, intr_number, irq, en, depth, desc->depth); } } mutex_unlock(&gm.pmic_intr_mutex); return 0; } bool is_battery_init_done(void) { return gm.is_probe_done; } bool is_recovery_mode(void) { int boot_mode = battery_get_boot_mode(); if (is_fg_disabled()) return false; bm_debug("mtk_battery boot mode =%d\n", boot_mode); if (boot_mode == RECOVERY_BOOT) { gm.log_level = BMLOG_DEBUG_LEVEL; fg_cust_data.daemon_log_level = BMLOG_DEBUG_LEVEL; return true; } return false; } int battery_get_boot_mode(void) { struct device *dev = NULL; struct device_node *boot_node = NULL; struct tag_bootmode *tag = NULL; int boot_mode = 11;//UNKNOWN_BOOT dev = gm.gdev->dev.parent; if (dev != NULL) { boot_node = of_parse_phandle(dev->of_node, "bootmode", 0); if (!boot_node) { bm_err("%s: failed to get boot mode phandle\n", __func__); } else { tag = (struct tag_bootmode *)of_get_property(boot_node, "atag,boot", NULL); if (!tag) bm_err("%s: failed to get atag,boot\n", __func__); else { boot_mode = tag->bootmode; gm.boot_mode = tag->bootmode; } } } bm_debug("%s: boot mode=%d\n", __func__, boot_mode); return boot_mode; } bool is_fg_disabled(void) { return gm.disableGM30; } bool set_charge_power_sel(enum CHARGE_SEL select) { /* select gm.charge_power_sel to CHARGE_NORMAL ,CHARGE_R1,CHARGE_R2 */ /* example: gm.charge_power_sel = CHARGE_NORMAL */ gm.charge_power_sel = select; wakeup_fg_algo_cmd(FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_FORCE_BAT_TEMP, select); return 0; } int dump_pseudo100(enum CHARGE_SEL select) { int i = 0; bm_err("%s:select=%d\n", __func__, select); if (select > MAX_CHARGE_RDC || select < 0) return 0; for (i = 0; i < MAX_TABLE; i++) { bm_err("%6d\n", fg_table_cust_data.fg_profile[i].r_pseudo100.pseudo[select]); } return 0; } int register_battery_notifier(struct notifier_block *nb) { int ret = 0; mutex_lock(&gm.notify_mutex); ret = srcu_notifier_chain_register(&gm.gm_notify, nb); mutex_unlock(&gm.notify_mutex); return ret; } int unregister_battery_notifier(struct notifier_block *nb) { int ret = 0; mutex_lock(&gm.notify_mutex); ret = srcu_notifier_chain_unregister(&gm.gm_notify, nb); mutex_unlock(&gm.notify_mutex); return ret; } int battery_notifier(int event) { return srcu_notifier_call_chain(&gm.gm_notify, event, NULL); } /* ============================================================ */ /* functions for fg hal*/ /* ============================================================ */ void set_hw_ocv_unreliable(bool _flag_unreliable) { gm.hw_status.flag_hw_ocv_unreliable = _flag_unreliable; } /* ============================================================ */ /* functions */ /* ============================================================ */ signed int battery_meter_get_tempR(signed int dwVolt) { int TRes; TRes = 0; if (is_fg_disabled()) return 0; TRes = (gm.rbat.rbat_pull_up_r * dwVolt) / (gm.rbat.rbat_pull_up_volt - dwVolt); return TRes; } signed int battery_meter_get_tempV(void) { int val = 0; if (is_fg_disabled()) return 0; val = pmic_get_v_bat_temp(); return val; } signed int battery_meter_get_VSense(void) { if (is_fg_disabled()) return 0; else return pmic_get_ibus(); } int check_cap_level(int uisoc) { if (uisoc >= 100) return POWER_SUPPLY_CAPACITY_LEVEL_FULL; else if (uisoc >= 80 && uisoc < 100) return POWER_SUPPLY_CAPACITY_LEVEL_HIGH; else if (uisoc >= 20 && uisoc < 80) return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; else if (uisoc > 0 && uisoc < 20) return POWER_SUPPLY_CAPACITY_LEVEL_LOW; else if (uisoc == 0) return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; else return POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; } void battery_update_psd(struct battery_data *bat_data) { bat_data->BAT_batt_vol = battery_get_bat_voltage(); bat_data->BAT_batt_temp = battery_get_bat_temperature(); } #if defined(CONFIG_USB_FACTORY_MODE) #if defined(CONFIG_SEC_FACTORY) extern int mt6360_read_vsys_uvolt(void); static int vsys_to_vsoc_for_factory_mode(void) { int vsys_volt = 0; int index_value, vsoc_value = 0; vsys_volt = mt6360_read_vsys_uvolt() / 1000; for (index_value = 0; index_value < 100; index_value++) { if ((fg_table_cust_data.fg_profile[1].fg_profile[index_value].voltage / 10) < vsys_volt) { vsoc_value = 100 - index_value; break; } } pr_info("%s vsys_volt: %d, VSOC[%d] : %d\n", __func__, vsys_volt, vsoc_value, fg_table_cust_data.fg_profile[1].fg_profile[index_value].voltage / 10); return vsoc_value; } #endif #endif static int battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { int ret = 0; int fgcurrent = 0; bool b_ischarging = 0; #if defined(CONFIG_USB_FACTORY_MODE) union power_supply_propval value = {0, }; enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp; #endif struct battery_data *data = container_of(psy->desc, struct battery_data, psd); #if defined(CONFIG_BATTERY_GKI) switch ((int)psp) { #else switch (psp) { #endif case POWER_SUPPLY_PROP_STATUS: val->intval = data->BAT_STATUS; break; case POWER_SUPPLY_PROP_HEALTH: val->intval = data->BAT_HEALTH;/* do not change before*/ break; case POWER_SUPPLY_PROP_PRESENT: val->intval = data->BAT_PRESENT;/* do not change before*/ break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = data->BAT_TECHNOLOGY; break; case POWER_SUPPLY_PROP_CYCLE_COUNT: val->intval = gm.bat_cycle; break; case POWER_SUPPLY_PROP_CAPACITY: #if defined(CONFIG_BATTERY_SAMSUNG) if (val->intval == SEC_FUELGAUGE_CAPACITY_TYPE_RAW) { val->intval = gm.precise_soc * 10; } else if (val->intval == SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE) { val->intval = battery_get_precise_uisoc(); pr_info("%s : ui SOC(%d%%)\n", __func__, val->intval); } else { #endif /* 1 = META_BOOT, 4 = FACTORY_BOOT 5=ADVMETA_BOOT */ /* 6= ATE_factory_boot */ if (gm.boot_mode == 1 || gm.boot_mode == 4 || gm.boot_mode == 5 || gm.boot_mode == 6) { val->intval = 75; break; } if (gm.fixed_uisoc != 0xffff) val->intval = gm.fixed_uisoc; else val->intval = data->BAT_CAPACITY; #if defined(CONFIG_BATTERY_SAMSUNG) if (val->intval < 0) { val->intval = battery_get_soc(); pr_info("%s : real / fake capacity(%d%%/%d%%)\n", __func__, val->intval, SEC_BATTERY_FAKE_CAPACITY); val->intval = SEC_BATTERY_FAKE_CAPACITY; gm.is_fake_soc = 1; } #endif #if defined(CONFIG_SEC_FACTORY) if (data->f_mode == OB_MODE) val->intval = vsys_to_vsoc_for_factory_mode(); #endif #if defined(CONFIG_BATTERY_SAMSUNG) } #endif break; case POWER_SUPPLY_PROP_CURRENT_NOW: b_ischarging = gauge_get_current(&fgcurrent); if (b_ischarging == false) fgcurrent = 0 - fgcurrent; #if defined(CONFIG_BATTERY_SAMSUNG) if (val->intval == SEC_BATTERY_CURRENT_UA) val->intval = fgcurrent * 100; else val->intval = fgcurrent / 10; #else val->intval = fgcurrent * 100; #endif break; case POWER_SUPPLY_PROP_CURRENT_AVG: #if defined(CONFIG_BATTERY_SAMSUNG) if (val->intval == SEC_BATTERY_CURRENT_UA) val->intval = battery_get_bat_avg_current() * 100; else val->intval = battery_get_bat_avg_current() / 10; #else val->intval = battery_get_bat_avg_current() * 100; #endif break; case POWER_SUPPLY_PROP_CHARGE_FULL: val->intval = fg_table_cust_data.fg_profile[gm.battery_id].q_max * 1000; break; case POWER_SUPPLY_PROP_CHARGE_COUNTER: val->intval = gm.ui_soc * fg_table_cust_data.fg_profile[gm.battery_id].q_max * 1000 / 100; break; #if defined(CONFIG_BATTERY_SAMSUNG) case POWER_SUPPLY_PROP_VOLTAGE_AVG: #endif case POWER_SUPPLY_PROP_VOLTAGE_NOW: #if defined(CONFIG_USB_FACTORY_MODE) if (data->f_mode == OB_MODE) { psy_do_property("mtk-charger", get, POWER_SUPPLY_EXT_PROP_BATT_VSYS, value); val->intval = value.intval; } else val->intval = battery_get_bat_voltage(); #else val->intval = data->BAT_batt_vol * 1000; #endif break; #if defined(CONFIG_BATTERY_SAMSUNG) case POWER_SUPPLY_PROP_TEMP_AMBIENT: #endif case POWER_SUPPLY_PROP_TEMP: #if defined(CONFIG_BATTERY_SAMSUNG) force_get_tbat_adc(); val->intval = gm.tbat_adc; pr_info("%s : val->intval (%d)\n", __func__, val->intval); #else val->intval = gm.tbat_precise; #endif break; #ifdef CONFIG_BATTERY_SAMSUNG case POWER_SUPPLY_PROP_ENERGY_NOW: switch (val->intval) { case SEC_BATTERY_CAPACITY_FULL: val->intval = fg_table_cust_data.fg_profile[gm.battery_id].q_max; pr_info("[%s] fg_table_cust_data.fg_profile[gm.battery_id].q_max %d\n", __func__, fg_table_cust_data.fg_profile[gm.battery_id].q_max); break; } break; case POWER_SUPPLY_PROP_ENERGY_FULL: val->intval = gm.aging_factor / 100; break; #endif case POWER_SUPPLY_PROP_CAPACITY_LEVEL: val->intval = check_cap_level(data->BAT_CAPACITY); break; case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: /* full or unknown must return 0 */ ret = check_cap_level(data->BAT_CAPACITY); if ((ret == POWER_SUPPLY_CAPACITY_LEVEL_FULL) || (ret == POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN)) val->intval = 0; else { int q_max_now = fg_table_cust_data.fg_profile[ gm.battery_id].q_max; int remain_ui = 100 - data->BAT_CAPACITY; int remain_mah = remain_ui * q_max_now / 10; int time_to_full = 0; gauge_get_current(&fgcurrent); if (fgcurrent != 0) time_to_full = remain_mah * 3600 / fgcurrent; bm_debug("time_to_full:%d, remain:ui:%d mah:%d, fgcurrent:%d, qmax:%d\n", time_to_full, remain_ui, remain_mah, fgcurrent, q_max_now); val->intval = abs(time_to_full); } ret = 0; break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: if (check_cap_level(data->BAT_CAPACITY) == POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN) val->intval = 0; else { int q_max_mah = 0; int q_max_uah = 0; q_max_mah = fg_table_cust_data.fg_profile[ gm.battery_id].q_max / 10; q_max_uah = q_max_mah * 1000; if (q_max_uah <= 100000) { bm_debug("%s q_max_mah:%d q_max_uah:%d\n", __func__, q_max_mah, q_max_uah); q_max_uah = 100001; } val->intval = q_max_uah; } break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: val->intval = gm.dynamic_cv; break; #ifdef CONFIG_BATTERY_SAMSUNG case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: break; case POWER_SUPPLY_PROP_VOLTAGE_OCV: break; case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX: switch (ext_psp) { case POWER_SUPPLY_EXT_PROP_PMIC_BAT_VOLTAGE: { int j, k, ocv, ocv_data[10]; for (j = 0; j < 10; j++) ocv_data[j] = pmic_get_battery_voltage(); for (j = 1; j < 10; j++) { ocv = ocv_data[j]; k = j; while (k > 0 && ocv_data[k-1] > ocv) { ocv_data[k] = ocv_data[k-1]; k--; } ocv_data[k] = ocv; } for (j = 0; j < 10; j++) pr_info("%s: [%d] %d\n", __func__, j, ocv_data[j]); ocv = 0; for (j = 2; j < 8; j++) ocv += ocv_data[j]; val->intval = ocv / 6; } break; case POWER_SUPPLY_EXT_PROP_MONITOR_WORK: break; case POWER_SUPPLY_EXT_PROP_BATTERY_ID: val->intval = gm.battery_id; break; case POWER_SUPPLY_EXT_PROP_BATT_DUMP: { memset(data->d_buf, 0x0, sizeof(data->d_buf)); /* capacity_max not supported, set to 0 */ snprintf(data->d_buf + strlen(data->d_buf), sizeof(data->d_buf), "%d,%d,%d", pmic_get_battery_voltage(), gm.precise_soc * 10, 0); val->strval = data->d_buf; } break; default: return -EINVAL; } break; #endif default: ret = -EINVAL; break; } return ret; } #if defined(CONFIG_USB_FACTORY_MODE) extern void set_g_low_battery_stop(int val); #endif static int battery_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { int ret = 0; #ifdef CONFIG_BATTERY_SAMSUNG enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp; struct battery_data *data = container_of(psy->desc, struct battery_data, psd); #endif #if defined(CONFIG_BATTERY_GKI) switch ((int)psp) { #else switch (psp) { #endif #ifdef CONFIG_BATTERY_SAMSUNG case POWER_SUPPLY_PROP_STATUS: if (val->intval == POWER_SUPPLY_STATUS_FULL) { notify_fg_chr_full(); gm.is_full = 0; pr_info("%s: Battery Full!\n", __func__); } break; case POWER_SUPPLY_PROP_CHARGE_FULL: /* No need to run if SOC is already 100% */ if (val->intval == 100) break; if (gm.is_full != 1) { notify_fg_chr_full(); gm.is_full = 1; pr_info("%s: Force Battery Full!\n", __func__); } break; case POWER_SUPPLY_PROP_CHARGING_ENABLED: if (val->intval == SEC_BAT_CHG_MODE_CHARGING) { pr_info("%s: Battery Charging!\n", __func__); fg_sw_bat_cycle_accu(); battery_main.BAT_STATUS = POWER_SUPPLY_STATUS_CHARGING; battery_update(&battery_main); } else { pr_info("%s: Battery Discharging!\n", __func__); gm.is_full = 0; fg_sw_bat_cycle_accu(); battery_main.BAT_STATUS = POWER_SUPPLY_STATUS_DISCHARGING; battery_update(&battery_main); } break; case POWER_SUPPLY_PROP_TEMP: case POWER_SUPPLY_PROP_TEMP_AMBIENT: case POWER_SUPPLY_PROP_ONLINE: case POWER_SUPPLY_PROP_CAPACITY_LEVEL: break; case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX: switch (ext_psp) { #if IS_ENABLED(CONFIG_BATTERY_GKI) case POWER_SUPPLY_EXT_PROP_CHARGING_ENABLED: if (val->intval == SEC_BAT_CHG_MODE_CHARGING) { pr_info("%s: Battery Charging!\n", __func__); fg_sw_bat_cycle_accu(); battery_main.BAT_STATUS = POWER_SUPPLY_STATUS_CHARGING; battery_update(&battery_main); } else { pr_info("%s: Battery Discharging!\n", __func__); gm.is_full = 0; fg_sw_bat_cycle_accu(); battery_main.BAT_STATUS = POWER_SUPPLY_STATUS_DISCHARGING; battery_update(&battery_main); } break; #endif case POWER_SUPPLY_EXT_PROP_BATT_F_MODE: data->f_mode = val->intval; pr_info("%s: mtk-fg-battery: FG f_mode: %s\n", __func__, BOOT_MODE_STRING[data->f_mode]); #if defined(CONFIG_SEC_FACTORY) if (data->f_mode == OB_MODE) { set_g_low_battery_stop(1); disable_fg(); } #endif break; default: return -EINVAL; } break; #endif case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: if (val->intval > 0) { gm.dynamic_cv = val->intval / 100; wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_GET_DYNAMIC_CV, gm.dynamic_cv); bm_err("[%s], dynamic_cv: %d\n", __func__, gm.dynamic_cv); } break; default: ret = -EINVAL; break; } bm_debug("%s psp:%d ret:%d val:%d", __func__, psp, ret, val->intval); return ret; } /* battery_data initialization */ struct battery_data battery_main = { .psd = { #if defined(CONFIG_BATTERY_SAMSUNG) .name = "mtk-fg-battery", .type = POWER_SUPPLY_TYPE_UNKNOWN, #else .name = "battery", .type = POWER_SUPPLY_TYPE_BATTERY, #endif .properties = battery_props, .num_properties = ARRAY_SIZE(battery_props), .get_property = battery_get_property, .set_property = battery_set_property, }, .BAT_STATUS = POWER_SUPPLY_STATUS_DISCHARGING, .BAT_HEALTH = POWER_SUPPLY_HEALTH_GOOD, .BAT_PRESENT = 1, .BAT_TECHNOLOGY = POWER_SUPPLY_TECHNOLOGY_LION, .BAT_CAPACITY = -1, .BAT_batt_vol = 0, .BAT_batt_temp = 0, }; void evb_battery_init(void) { battery_main.BAT_STATUS = POWER_SUPPLY_STATUS_FULL; battery_main.BAT_HEALTH = POWER_SUPPLY_HEALTH_GOOD; battery_main.BAT_PRESENT = 1; battery_main.BAT_TECHNOLOGY = POWER_SUPPLY_TECHNOLOGY_LION; battery_main.BAT_CAPACITY = 100; battery_main.BAT_batt_vol = 4200; battery_main.BAT_batt_temp = 22; } static void disable_fg(void) { int fgv; fgv = gauge_get_hw_version(); if (fgv >= GAUGE_HW_V1000 && fgv < GAUGE_HW_V2000) { en_intr_VBATON_UNDET(0); } gauge_enable_interrupt(FG_BAT1_INT_L_NO, 0); gauge_enable_interrupt(FG_BAT1_INT_H_NO, 0); gauge_enable_interrupt(FG_BAT0_INT_L_NO, 0); gauge_enable_interrupt(FG_BAT0_INT_H_NO, 0); gauge_enable_interrupt(FG_N_CHARGE_L_NO, 0); gauge_enable_interrupt(FG_IAVG_H_NO, 0); gauge_enable_interrupt(FG_IAVG_L_NO, 0); gauge_enable_interrupt(FG_ZCV_NO, 0); gauge_enable_interrupt(FG_BAT_PLUGOUT_NO, 0); gauge_enable_interrupt(FG_RG_INT_EN_NAG_C_DLTV, 0); gauge_enable_interrupt(FG_RG_INT_EN_BAT_TEMP_H, 0); gauge_enable_interrupt(FG_RG_INT_EN_BAT_TEMP_L, 0); gauge_enable_interrupt(FG_RG_INT_EN_BAT2_H, 0); gauge_enable_interrupt(FG_RG_INT_EN_BAT2_L, 0); gm.disableGM30 = 1; gm.ui_soc = 50; battery_main.BAT_CAPACITY = 50; } bool fg_interrupt_check(void) { if (is_fg_disabled()) { disable_fg(); return false; } return true; } void battery_update(struct battery_data *bat_data) { #ifdef CONFIG_BATTERY_SAMSUNG union power_supply_propval value; int ui_soc_value = 0; struct power_supply *psy; int ret = 0; #endif #if !defined(CONFIG_BATTERY_SAMSUNG) struct power_supply *bat_psy = bat_data->psy; #endif battery_update_psd(&battery_main); bat_data->BAT_TECHNOLOGY = POWER_SUPPLY_TECHNOLOGY_LION; bat_data->BAT_HEALTH = POWER_SUPPLY_HEALTH_GOOD; bat_data->BAT_PRESENT = 1; #if defined(CONFIG_MTK_DISABLE_GAUGE) return; #endif if (is_fg_disabled()) bat_data->BAT_CAPACITY = 50; #ifdef CONFIG_BATTERY_SAMSUNG psy = power_supply_get_by_name("battery"); if (!psy) { pr_err("%s: Fail to get psy (battery)\n", __func__); } else { if (gm.is_fake_soc) { ui_soc_value = battery_get_uisoc(); if (ui_soc_value >= 0) { pr_info("%s : clear fake SOC\n", __func__); value.intval = ui_soc_value; ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_CAPACITY, &value); if (ret < 0) pr_err("%s: psy capacity fail(%d)\n", __func__, ret); #if IS_ENABLED(CONFIG_DIRECT_CHARGING) value.intval = 1; ret = power_supply_set_property(psy, (enum power_supply_property) POWER_SUPPLY_EXT_PROP_MTK_FG_INIT, &value); if (ret < 0) pr_err("%s: psy update fg state fail(%d)\n", __func__, ret); #endif gm.is_fake_soc = 0; value.intval = 0; ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_TYPE, &value); if (ret < 0) pr_err("%s: psy online fail(%d)\n", __func__, ret); } } } #endif #if !defined(CONFIG_BATTERY_SAMSUNG) power_supply_changed(bat_psy); #endif } bool is_kernel_power_off_charging(void) { int boot_mode = battery_get_boot_mode(); if (boot_mode == KERNEL_POWER_OFF_CHARGING_BOOT || boot_mode == LOW_POWER_OFF_CHARGING_BOOT) { return true; } return false; } int bat_get_debug_level(void) { return gm.log_level; } /* ============================================================ */ /* function prototype */ /* ============================================================ */ static void nl_send_to_user(u32 pid, int seq, struct fgd_nl_msg_t *reply_msg); struct fuel_gauge_custom_data fg_cust_data; struct fuel_gauge_table_custom_data fg_table_cust_data; /* ============================================================ */ /* extern function */ /* ============================================================ */ static void proc_dump_log(struct seq_file *m) { seq_printf(m, "subcmd:%d para1:%d\n", gm.proc_subcmd, gm.proc_subcmd_para1); seq_printf(m, "%s\n", gm.proc_log); } static void proc_dump_dtsi(struct seq_file *m) { int i; seq_puts(m, "********** dump DTSI **********\n"); seq_printf(m, "Active Table :%d\n", fg_table_cust_data.active_table_number); for (i = 0; i < fg_table_cust_data.active_table_number; i++) { seq_printf(m, "PMIC_MIN_VOL = %d\n", fg_table_cust_data.fg_profile[i].pmic_min_vol); } for (i = 0; i < fg_table_cust_data.active_table_number; i++) { seq_printf(m, "POWERON_SYSTEM_IBOOT = %d\n", fg_table_cust_data.fg_profile[i].pon_iboot); } for (i = 0; i < fg_table_cust_data.active_table_number; i++) { seq_printf(m, "TEMPERATURE_T%d = %d\n", i, fg_table_cust_data.fg_profile[i].temperature); } for (i = 0; i < fg_table_cust_data.active_table_number; i++) { seq_printf(m, "g_FG_PSEUDO100_%d = %d\n", i, fg_table_cust_data.fg_profile[i].pseudo100); } seq_printf(m, "DIFFERENCE_FULLOCV_ITH = %d\n", fg_cust_data.difference_fullocv_ith); seq_printf(m, "SHUTDOWN_1_TIME = %d\n", fg_cust_data.shutdown_1_time); seq_printf(m, "KEEP_100_PERCENT = %d\n", fg_cust_data.keep_100_percent_minsoc); seq_printf(m, "R_FG_VALUE = %d\n", fg_cust_data.r_fg_value); seq_printf(m, "EMBEDDED_SEL = %d\n", fg_cust_data.embedded_sel); seq_printf(m, "PMIC_SHUTDOWN_CURRENT = %d\n", fg_cust_data.pmic_shutdown_current); seq_printf(m, "FG_METER_RESISTANCE = %d\n", fg_cust_data.fg_meter_resistance); seq_printf(m, "CAR_TUNE_VALUE = %d\n", fg_cust_data.car_tune_value); seq_printf(m, "SHUTDOWN_GAUGE0_VOLTAGE = %d\n", fg_cust_data.shutdown_gauge0_voltage); seq_printf(m, "Q_MAX_SYS_VOLTAGE = %d\n", fg_cust_data.q_max_sys_voltage); seq_printf(m, "COM_FG_METER_RESISTANCE = %d\n", fg_cust_data.com_fg_meter_resistance); seq_printf(m, "COM_R_FG_VALUE = %d\n", fg_cust_data.com_r_fg_value); seq_printf(m, "enable_tmp_intr_suspend = %d\n", gm.enable_tmp_intr_suspend); seq_printf(m, "ACTIVE_TABLE = %d\n", fg_table_cust_data.active_table_number); seq_printf(m, "MULTI_TEMP_GAUGE0 = %d\n", fg_cust_data.multi_temp_gauge0); seq_printf(m, "SHUTDOWN_GAUGE0 = %d\n", fg_cust_data.shutdown_gauge0); seq_printf(m, "SHUTDOWN_GAUGE1_XMINS = %d\n", fg_cust_data.shutdown_gauge1_xmins); seq_printf(m, "SHUTDOWN_GAUGE1_VBAT_EN = %d\n", fg_cust_data.shutdown_gauge1_vbat_en); seq_printf(m, "SHUTDOWN_GAUGE1_VBAT = %d\n", fg_cust_data.shutdown_gauge1_vbat); seq_printf(m, "PSEUDO100_EN = %d\n", fg_cust_data.pseudo100_en); seq_printf(m, "PSEUDO100_EN_DIS = %d\n", fg_cust_data.pseudo100_en_dis); seq_printf(m, "CHARGE_PSEUDO_FULL_LEVEL = %d\n", fg_cust_data.charge_pseudo_full_level); seq_printf(m, "FULL_TRACKING_BAT_INT2_MULTIPLY = %d\n", fg_cust_data.full_tracking_bat_int2_multiply); seq_printf(m, "DISCHARGE_TRACKING_TIME = %d\n", fg_cust_data.discharge_tracking_time); seq_printf(m, "CHARGE_TRACKING_TIME = %d\n", fg_cust_data.charge_tracking_time); seq_printf(m, "DIFFERENCE_FULLOCV_VTH = %d\n", fg_cust_data.difference_fullocv_vth); seq_printf(m, "HWOCV_SWOCV_DIFF = %d\n", fg_cust_data.hwocv_swocv_diff); seq_printf(m, "HWOCV_SWOCV_DIFF_LT = %d\n", fg_cust_data.hwocv_swocv_diff_lt); seq_printf(m, "HWOCV_SWOCV_DIFF_LT_TEMP = %d\n", fg_cust_data.hwocv_swocv_diff_lt_temp); seq_printf(m, "HWOCV_OLDOCV_DIFF = %d\n", fg_cust_data.hwocv_oldocv_diff); seq_printf(m, "HWOCV_OLDOCV_DIFF_CHR = %d\n", fg_cust_data.hwocv_oldocv_diff_chr); seq_printf(m, "VBAT_OLDOCV_DIFF = %d\n", fg_cust_data.vbat_oldocv_diff); seq_printf(m, "SWOCV_OLDOCV_DIFF_EMB = %d\n", fg_cust_data.swocv_oldocv_diff_emb); seq_printf(m, "TNEW_TOLD_PON_DIFF = %d\n", fg_cust_data.tnew_told_pon_diff); seq_printf(m, "TNEW_TOLD_PON_DIFF2 = %d\n", fg_cust_data.tnew_told_pon_diff2); seq_printf(m, "PMIC_SHUTDOWN_TIME = %d\n", fg_cust_data.pmic_shutdown_time); seq_printf(m, "EXT_HWOCV_SWOCV = %d\n", gm.ext_hwocv_swocv); seq_printf(m, "EXT_HWOCV_SWOCV_LT = %d\n", gm.ext_hwocv_swocv_lt); seq_printf(m, "EXT_HWOCV_SWOCV_LT_TEMP = %d\n", gm.ext_hwocv_swocv_lt_temp); seq_printf(m, "DIFFERENCE_FGC_FGV_TH1 = %d\n", fg_cust_data.difference_fgc_fgv_th1); seq_printf(m, "DIFFERENCE_FGC_FGV_TH2 = %d\n", fg_cust_data.difference_fgc_fgv_th2); seq_printf(m, "DIFFERENCE_FGC_FGV_TH3 = %d\n", fg_cust_data.difference_fgc_fgv_th3); seq_printf(m, "DIFFERENCE_FGC_FGV_TH_SOC1 = %d\n", fg_cust_data.difference_fgc_fgv_th_soc1); seq_printf(m, "DIFFERENCE_FGC_FGV_TH_SOC2 = %d\n", fg_cust_data.difference_fgc_fgv_th_soc2); seq_printf(m, "PMIC_SHUTDOWN_SW_EN = %d\n", fg_cust_data.pmic_shutdown_sw_en); seq_printf(m, "FORCE_VC_MODE = %d\n", fg_cust_data.force_vc_mode); seq_printf(m, "ZCV_SUSPEND_TIME = %d\n", fg_cust_data.zcv_suspend_time); seq_printf(m, "SLEEP_CURRENT_AVG = %d\n", fg_cust_data.sleep_current_avg); seq_printf(m, "ZCV_CAR_GAP_PERCENTAGE = %d\n", fg_cust_data.zcv_car_gap_percentage); seq_printf(m, "UI_FULL_LIMIT_EN = %d\n", fg_cust_data.ui_full_limit_en); seq_printf(m, "UI_FULL_LIMIT_SOC0 = %d\n", fg_cust_data.ui_full_limit_soc0); seq_printf(m, "UI_FULL_LIMIT_ITH0 = %d\n", fg_cust_data.ui_full_limit_ith0); seq_printf(m, "UI_FULL_LIMIT_SOC1 = %d\n", fg_cust_data.ui_full_limit_soc1); seq_printf(m, "UI_FULL_LIMIT_ITH1 = %d\n", fg_cust_data.ui_full_limit_ith1); seq_printf(m, "UI_FULL_LIMIT_SOC2 = %d\n", fg_cust_data.ui_full_limit_soc2); seq_printf(m, "UI_FULL_LIMIT_ITH2 = %d\n", fg_cust_data.ui_full_limit_ith2); seq_printf(m, "UI_FULL_LIMIT_SOC3 = %d\n", fg_cust_data.ui_full_limit_soc3); seq_printf(m, "UI_FULL_LIMIT_ITH3 = %d\n", fg_cust_data.ui_full_limit_ith3); seq_printf(m, "UI_FULL_LIMIT_SOC4 = %d\n", fg_cust_data.ui_full_limit_soc4); seq_printf(m, "UI_FULL_LIMIT_ITH4 = %d\n", fg_cust_data.ui_full_limit_ith4); seq_printf(m, "UI_FULL_LIMIT_TIME = %d\n", fg_cust_data.ui_full_limit_time); seq_printf(m, "UI_LOW_LIMIT_EN = %d\n", fg_cust_data.ui_low_limit_en); seq_printf(m, "UI_LOW_LIMIT_SOC0 = %d\n", fg_cust_data.ui_low_limit_soc0); seq_printf(m, "UI_LOW_LIMIT_VTH0 = %d\n", fg_cust_data.ui_low_limit_vth0); seq_printf(m, "UI_LOW_LIMIT_SOC1 = %d\n", fg_cust_data.ui_low_limit_soc1); seq_printf(m, "UI_LOW_LIMIT_VTH1 = %d\n", fg_cust_data.ui_low_limit_vth1); seq_printf(m, "UI_LOW_LIMIT_SOC2 = %d\n", fg_cust_data.ui_low_limit_soc2); seq_printf(m, "UI_LOW_LIMIT_VTH2 = %d\n", fg_cust_data.ui_low_limit_vth2); seq_printf(m, "UI_LOW_LIMIT_SOC3 = %d\n", fg_cust_data.ui_low_limit_soc3); seq_printf(m, "UI_LOW_LIMIT_VTH3 = %d\n", fg_cust_data.ui_low_limit_vth3); seq_printf(m, "UI_LOW_LIMIT_SOC4 = %d\n", fg_cust_data.ui_low_limit_soc4); seq_printf(m, "UI_LOW_LIMIT_VTH4 = %d\n", fg_cust_data.ui_low_limit_vth4); seq_printf(m, "UI_LOW_LIMIT_TIME = %d\n", fg_cust_data.ui_low_limit_time); seq_printf(m, "FG_PRE_TRACKING_EN = %d\n", fg_cust_data.fg_pre_tracking_en); seq_printf(m, "VBAT2_DET_TIME = %d\n", fg_cust_data.vbat2_det_time); seq_printf(m, "VBAT2_DET_COUNTERE = %d\n", fg_cust_data.vbat2_det_counter); seq_printf(m, "VBAT2_DET_VOLTAGE1 = %d\n", fg_cust_data.vbat2_det_voltage1); seq_printf(m, "VBAT2_DET_VOLTAGE2 = %d\n", fg_cust_data.vbat2_det_voltage2); seq_printf(m, "VBAT2_DET_VOLTAGE3 = %d\n", fg_cust_data.vbat2_det_voltage3); seq_printf(m, "AGING_FACTOR_MIN = %d\n", fg_cust_data.aging_factor_min); seq_printf(m, "AGING_FACTOR_DIFF = %d\n", fg_cust_data.aging_factor_diff); seq_printf(m, "DIFFERENCE_VOLTAGE_UPDATE = %d\n", fg_cust_data.difference_voltage_update); seq_printf(m, "AGING_ONE_EN = %d\n", fg_cust_data.aging_one_en); seq_printf(m, "AGING1_UPDATE_SOC = %d\n", fg_cust_data.aging1_update_soc); seq_printf(m, "AGING1_LOAD_SOC = %d\n", fg_cust_data.aging1_load_soc); seq_printf(m, "AGING4_UPDATE_SOC = %d\n", fg_cust_data.aging4_update_soc); seq_printf(m, "AGING4_LOAD_SOC = %d\n", fg_cust_data.aging4_load_soc); seq_printf(m, "AGING5_UPDATE_SOC = %d\n", fg_cust_data.aging5_update_soc); seq_printf(m, "AGING5_LOAD_SOC = %d\n", fg_cust_data.aging5_load_soc); seq_printf(m, "AGING6_UPDATE_SOC = %d\n", fg_cust_data.aging6_update_soc); seq_printf(m, "AGING6_LOAD_SOC = %d\n", fg_cust_data.aging6_load_soc); seq_printf(m, "AGING_TEMP_DIFF = %d\n", fg_cust_data.aging_temp_diff); seq_printf(m, "AGING_100_EN = %d\n", fg_cust_data.aging_100_en); seq_printf(m, "AGING_TWO_EN = %d\n", fg_cust_data.aging_two_en); seq_printf(m, "AGING_THIRD_EN = %d\n", fg_cust_data.aging_third_en); seq_printf(m, "AGING_4_EN = %d\n", fg_cust_data.aging_4_en); seq_printf(m, "AGING_5_EN = %d\n", fg_cust_data.aging_5_en); seq_printf(m, "AGING_6_EN = %d\n", fg_cust_data.aging_6_en); seq_printf(m, "DIFF_SOC_SETTING = %d\n", fg_cust_data.diff_soc_setting); seq_printf(m, "DIFF_BAT_TEMP_SETTING = %d\n", fg_cust_data.diff_bat_temp_setting); seq_printf(m, "DIFF_BAT_TEMP_SETTING_C = %d\n", fg_cust_data.diff_bat_temp_setting_c); seq_printf(m, "DIFF_IAVG_TH = %d\n", fg_cust_data.diff_iavg_th); seq_printf(m, "FG_TRACKING_CURRENT = %d\n", fg_cust_data.fg_tracking_current); seq_printf(m, "FG_TRACKING_CURRENT_IBOOT_EN = %d\n", fg_cust_data.fg_tracking_current_iboot_en); seq_printf(m, "UISOC_UPDATE_TYPE = %d\n", fg_cust_data.uisoc_update_type); seq_printf(m, "OVER_DISCHARGE_LEVEL = %d\n", fg_cust_data.over_discharge_level); seq_printf(m, "NAFG_TIME_SETTING = %d\n", fg_cust_data.nafg_time_setting); seq_printf(m, "NAFG_RATIO = %d\n", fg_cust_data.nafg_ratio); seq_printf(m, "NAFG_RATIO_EN = %d\n", fg_cust_data.nafg_ratio_en); seq_printf(m, "NAFG_RATIO_TMP_THR = %d\n", fg_cust_data.nafg_ratio_tmp_thr); seq_printf(m, "D0_SEL = %d\n", fg_cust_data.d0_sel); seq_printf(m, "IBOOT_SEL = %d\n", fg_cust_data.iboot_sel); seq_printf(m, "SHUTDOWN_SYSTEM_IBOOT = %d\n", fg_cust_data.shutdown_system_iboot); seq_printf(m, "DIFFERENCE_FULL_CV = %d\n", fg_cust_data.difference_full_cv); seq_printf(m, "PSEUDO1_EN = %d\n", fg_cust_data.pseudo1_en); seq_printf(m, "LOADING_1_EN = %d\n", fg_cust_data.loading_1_en); seq_printf(m, "LOADING_2_EN = %d\n", fg_cust_data.loading_2_en); seq_printf(m, "PSEUDO1_SEL = %d\n", fg_cust_data.pseudo1_sel); seq_printf(m, "UI_FAST_TRACKING_EN = %d\n", fg_cust_data.ui_fast_tracking_en); seq_printf(m, "UI_FAST_TRACKING_GAP = %d\n", fg_cust_data.ui_fast_tracking_gap); seq_printf(m, "KEEP_100_PERCENT_MINSOC = %d\n", fg_cust_data.keep_100_percent_minsoc); seq_printf(m, "NO_BAT_TEMP_COMPENSATE = %d\n", gm.no_bat_temp_compensate); seq_printf(m, "LOW_TEMP_MODE = %d\n", fg_cust_data.low_temp_mode); seq_printf(m, "LOW_TEMP_MODE_TEMP = %d\n", fg_cust_data.low_temp_mode_temp); seq_printf(m, "Q_MAX_L_CURRENT = %d\n", fg_cust_data.q_max_L_current); seq_printf(m, "Q_MAX_H_CURRENT = %d\n", fg_cust_data.q_max_H_current); seq_printf(m, "pl_two_sec_reboot = %d\n", gm.pl_two_sec_reboot); #ifdef SHUTDOWN_CONDITION_LOW_BAT_VOLT seq_puts(m, "SHUTDOWN_CONDITION_LOW_BAT_VOLT = 1\n"); seq_printf(m, "lbat_def: %d %d %d\n", VBAT2_DET_VOLTAGE1, VBAT2_DET_VOLTAGE2, VBAT2_DET_VOLTAGE3); seq_printf(m, "lbat: %d %d %d\n", fg_cust_data.vbat2_det_voltage1, fg_cust_data.vbat2_det_voltage2, fg_cust_data.vbat2_det_voltage3); #else seq_puts(m, "SHUTDOWN_CONDITION_LOW_BAT_VOLT = 0\n"); #endif seq_printf(m, "hw_version = %d\n", gauge_get_hw_version()); seq_printf(m, "DYNAMIC_CV_FACTOR = %d\n", fg_cust_data.dynamic_cv_factor); seq_printf(m, "CHARGER_IEOC = %d\n", fg_cust_data.charger_ieoc); } static void dump_daemon_table(struct seq_file *m) { int i, j; struct FUELGAUGE_PROFILE_STRUCT *ptr; struct fuel_gauge_table_custom_data *ptable2; struct fuel_gauge_table *pfgt; ptable2 = &gm.fg_data.fg_table_cust_data; for (j = 0; j < fg_table_cust_data.active_table_number; j++) { pfgt = &gm.fg_data.fg_table_cust_data.fg_profile[j]; seq_printf(m, "daemon table idx:%d size:%d\n", j, pfgt->size); seq_printf(m, "tmp:%d qmax:%d %d pseudo1:%d pseudo100:%d\n", pfgt->temperature, pfgt->q_max, pfgt->q_max_h_current, pfgt->pseudo1, pfgt->pseudo100); seq_printf(m, "pmic_min_vol:%d pon_iboot:%d qmax_sys_vol:%d sd_hl_zcv:%d\n", pfgt->pmic_min_vol, pfgt->pon_iboot, pfgt->qmax_sys_vol, pfgt->shutdown_hl_zcv); seq_puts(m, "idx: maH, voltage, R1, R2, percentage\n"); ptr = &ptable2->fg_profile[j].fg_profile[0]; for (i = 0; i < 100; i++) { seq_printf(m, "%d: %d %d %d %d %d\n", i, ptr[i].mah, ptr[i].voltage, ptr[i].resistance, ptr[i].charge_r.rdc[0], ptr[i].percentage); } } seq_printf(m, "\ndaemon table idx:tmp0 size:%d\n", gm.fg_data.fg_table_cust_data.fg_profile_temperature_0_size); seq_puts(m, "idx: maH, voltage, R1, R2, percentage\n"); ptr = &gm.fg_data.fg_table_cust_data.fg_profile_temperature_0[0]; for (i = 0; i < 100; i++) { seq_printf(m, "%d: %d %d %d %d %d\n", i, ptr[i].mah, ptr[i].voltage, ptr[i].resistance, ptr[i].charge_r.rdc[0], ptr[i].percentage); } seq_printf(m, "\ndaemon table idx:tmp1 size:%d\n", gm.fg_data.fg_table_cust_data.fg_profile_temperature_1_size); seq_puts(m, "idx: maH, voltage, R, R2, percentage\n"); ptr = &gm.fg_data.fg_table_cust_data.fg_profile_temperature_1[0]; for (i = 0; i < 100; i++) { seq_printf(m, "%d: %d %d %d %d %d\n", i, ptr[i].mah, ptr[i].voltage, ptr[i].resistance, ptr[i].charge_r.rdc[0], ptr[i].percentage); } } static void dump_kernel_table(struct seq_file *m) { int i, j; struct FUELGAUGE_PROFILE_STRUCT *ptr; struct fuel_gauge_table_custom_data *ptable1; struct fuel_gauge_table *pfgt; ptable1 = &fg_table_cust_data; seq_printf(m, "tables no:%d table size:%d\n", fg_table_cust_data.active_table_number, fg_table_cust_data.fg_profile[0].size); for (j = 0; j < fg_table_cust_data.active_table_number; j++) { pfgt = &ptable1->fg_profile[j]; ptr = &ptable1->fg_profile[j].fg_profile[0]; seq_printf(m, "table idx:%d size:%d\n", j, pfgt->size); seq_printf(m, "tmp:%d qmax:%d %d pseudo1:%d pseudo100:%d\n", pfgt->temperature, pfgt->q_max, pfgt->q_max_h_current, pfgt->pseudo1, pfgt->pseudo100); seq_printf(m, "pmic_min_vol:%d pon_iboot:%d qmax_sys_vol:%d sd_hl_zcv:%d\n", pfgt->pmic_min_vol, pfgt->pon_iboot, pfgt->qmax_sys_vol, pfgt->shutdown_hl_zcv); seq_puts(m, "idx: maH, voltage, R1, R2, percentage\n"); for (i = 0; i < 100; i++) { seq_printf(m, "%d: %d %d %d %d %d\n", i, ptr[i].mah, ptr[i].voltage, ptr[i].resistance, ptr[i].charge_r.rdc[0], ptr[i].percentage); } } seq_puts(m, "\n"); if (is_recovery_mode() == false) { dump_daemon_table(m); } else { ptr = &ptable1->fg_profile_temperature_1[0]; seq_puts(m, "tmp1 idx: maH, voltage, R1, R2, percentage\n"); for (i = 0; i < 100; i++) { seq_printf(m, "%d: %d %d %d %d %d\n", i, ptr[i].mah, ptr[i].voltage, ptr[i].resistance, ptr[i].charge_r.rdc[0], ptr[i].percentage); } } } static int proc_dump_log_show(struct seq_file *m, void *v) { int i; seq_puts(m, "********** Gauge Dump **********\n"); seq_puts(m, "Command Table list\n"); seq_puts(m, "0: dump dtsi\n"); seq_puts(m, "1: dump v-mode table\n"); seq_puts(m, "101: dump gauge hw register\n"); seq_puts(m, "102: kernel table\n"); seq_puts(m, "103: send CHR FULL\n"); seq_puts(m, "104: disable NAFG interrupt\n"); seq_puts(m, "105: show daemon pid\n"); seq_printf(m, "current command:%d\n", gm.proc_cmd_id); switch (gm.proc_cmd_id) { case 0: proc_dump_dtsi(m); break; case 1: case 2: case 3: case 4: wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_DUMP_LOG, gm.proc_cmd_id); for (i = 0; i < 5; i++) { msleep(500); if (gm.proc_subcmd_para1 == 1) break; } proc_dump_log(m); break; case 101: gauge_dev_dump(gm.gdev, m, 0); break; case 102: dump_kernel_table(m); break; case 103: wakeup_fg_algo(FG_INTR_CHR_FULL); break; case 104: gauge_set_nag_en(false); gm.disable_nafg_int = true; break; case 105: seq_printf(m, "Gauge daemon pid:%d\n", gm.g_fgd_pid); break; default: seq_printf(m, "do not support command:%d\n", gm.proc_cmd_id); break; } /*battery_dump_info(m);*/ return 0; } static ssize_t proc_write( struct file *file, const char __user *buffer, size_t count, loff_t *f_pos) { int cmd = 0; char num[10]; memset(num, 0, 10); if (!count) return 0; if (count > (sizeof(num) - 1)) return -EINVAL; if (copy_from_user(num, buffer, count)) return -EFAULT; if (kstrtoint(num, 10, &cmd) == 0) gm.proc_cmd_id = cmd; else { gm.proc_cmd_id = 0; return -EFAULT; } bm_err("%s success %d\n", __func__, cmd); return count; } static int proc_dump_log_open(struct inode *inode, struct file *file) { return single_open(file, proc_dump_log_show, NULL); } static const struct file_operations battery_dump_log_proc_fops = { .open = proc_dump_log_open, .read = seq_read, .llseek = seq_lseek, .write = proc_write, }; void battery_debug_init(void) { struct proc_dir_entry *battery_dir; battery_dir = proc_mkdir("battery", NULL); if (!battery_dir) { bm_err("fail to mkdir /proc/battery\n"); return; } proc_create("dump_log", 0644, battery_dir, &battery_dump_log_proc_fops); } static ssize_t show_Battery_Temperature( struct device *dev, struct device_attribute *attr, char *buf) { bm_err("%s: %d %d\n", __func__, battery_main.BAT_batt_temp, gm.fixed_bat_tmp); return sprintf(buf, "%d\n", gm.fixed_bat_tmp); } static ssize_t store_Battery_Temperature( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { signed int temp; if (kstrtoint(buf, 10, &temp) == 0) { if (temp > 58 || temp < -10) { bm_err( "%s: setting tmp:%d!,reject set\n", __func__, temp); return size; } gm.fixed_bat_tmp = temp; if (gm.fixed_bat_tmp == 0xffff) fg_bat_temp_int_internal(); else { gauge_dev_enable_battery_tmp_lt_interrupt( gm.gdev, 0, 0); gauge_dev_enable_battery_tmp_ht_interrupt( gm.gdev, 0, 0); wakeup_fg_algo(FG_INTR_BAT_TMP_C_HT); wakeup_fg_algo(FG_INTR_BAT_TMP_HT); } battery_main.BAT_batt_temp = force_get_tbat(true); bm_err( "%s: fixed_bat_tmp:%d ,tmp:%d!\n", __func__, temp, battery_main.BAT_batt_temp); battery_update(&battery_main); } else { bm_err("%s: format error!\n", __func__); } return size; } static DEVICE_ATTR(Battery_Temperature, 0664, show_Battery_Temperature, store_Battery_Temperature); static ssize_t show_UI_SOC( struct device *dev, struct device_attribute *attr, char *buf) { bm_err("%s: %d %d\n", __func__, gm.ui_soc, gm.fixed_uisoc); return sprintf(buf, "%d\n", gm.fixed_uisoc); } static ssize_t store_UI_SOC( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { signed int temp; if (kstrtoint(buf, 10, &temp) == 0) { gm.fixed_uisoc = temp; bm_err("%s: %d %d\n", __func__, gm.ui_soc, gm.fixed_uisoc); battery_update(&battery_main); } return size; } static DEVICE_ATTR(UI_SOC, 0664, show_UI_SOC, store_UI_SOC); /* ============================================================ */ /* Internal function */ /* ============================================================ */ void fg_custom_data_check(void) { struct fuel_gauge_custom_data *p; p = &fg_cust_data; fgauge_get_profile_id(); bm_err("FGLOG MultiGauge0[%d] BATID[%d] pmic_min_vol[%d,%d,%d,%d,%d]\n", p->multi_temp_gauge0, gm.battery_id, fg_table_cust_data.fg_profile[0].pmic_min_vol, fg_table_cust_data.fg_profile[1].pmic_min_vol, fg_table_cust_data.fg_profile[2].pmic_min_vol, fg_table_cust_data.fg_profile[3].pmic_min_vol, fg_table_cust_data.fg_profile[4].pmic_min_vol); bm_err("FGLOG pon_iboot[%d,%d,%d,%d,%d] qmax_sys_vol[%d %d %d %d %d]\n", fg_table_cust_data.fg_profile[0].pon_iboot, fg_table_cust_data.fg_profile[1].pon_iboot, fg_table_cust_data.fg_profile[2].pon_iboot, fg_table_cust_data.fg_profile[3].pon_iboot, fg_table_cust_data.fg_profile[4].pon_iboot, fg_table_cust_data.fg_profile[0].qmax_sys_vol, fg_table_cust_data.fg_profile[1].qmax_sys_vol, fg_table_cust_data.fg_profile[2].qmax_sys_vol, fg_table_cust_data.fg_profile[3].qmax_sys_vol, fg_table_cust_data.fg_profile[4].qmax_sys_vol); } int interpolation(int i1, int b1, int i2, int b2, int i) { int ret; ret = (b2 - b1) * (i - i1) / (i2 - i1) + b1; return ret; } unsigned int TempConverBattThermistor(int temp) { int RES1 = 0, RES2 = 0; int TMP1 = 0, TMP2 = 0; int i; unsigned int TBatt_R_Value = 0xffff; if (temp >= Fg_Temperature_Table[20].BatteryTemp) { TBatt_R_Value = Fg_Temperature_Table[20].TemperatureR; } else if (temp <= Fg_Temperature_Table[0].BatteryTemp) { TBatt_R_Value = Fg_Temperature_Table[0].TemperatureR; } else { RES1 = Fg_Temperature_Table[0].TemperatureR; TMP1 = Fg_Temperature_Table[0].BatteryTemp; for (i = 0; i <= 20; i++) { if (temp <= Fg_Temperature_Table[i].BatteryTemp) { RES2 = Fg_Temperature_Table[i].TemperatureR; TMP2 = Fg_Temperature_Table[i].BatteryTemp; break; } { /* hidden else */ RES1 = Fg_Temperature_Table[i].TemperatureR; TMP1 = Fg_Temperature_Table[i].BatteryTemp; } } TBatt_R_Value = interpolation(TMP1, RES1, TMP2, RES2, temp); } bm_warn( "[%s] [%d] %d %d %d %d %d\n", __func__, TBatt_R_Value, TMP1, RES1, TMP2, RES2, temp); return TBatt_R_Value; } int BattThermistorConverTemp(int Res) { int i = 0; int RES1 = 0, RES2 = 0; int TBatt_Value = -2000, TMP1 = 0, TMP2 = 0; if (Res >= Fg_Temperature_Table[0].TemperatureR) { TBatt_Value = -400; } else if (Res <= Fg_Temperature_Table[20].TemperatureR) { TBatt_Value = 600; } else { RES1 = Fg_Temperature_Table[0].TemperatureR; TMP1 = Fg_Temperature_Table[0].BatteryTemp; for (i = 0; i <= 20; i++) { if (Res >= Fg_Temperature_Table[i].TemperatureR) { RES2 = Fg_Temperature_Table[i].TemperatureR; TMP2 = Fg_Temperature_Table[i].BatteryTemp; break; } { /* hidden else */ RES1 = Fg_Temperature_Table[i].TemperatureR; TMP1 = Fg_Temperature_Table[i].BatteryTemp; } } TBatt_Value = (((Res - RES2) * TMP1) + ((RES1 - Res) * TMP2)) * 10 / (RES1 - RES2); } bm_trace( "[%s] %d %d %d %d %d %d\n", __func__, RES1, RES2, Res, TMP1, TMP2, TBatt_Value); return TBatt_Value; } unsigned int TempToBattVolt(int temp, int update) { unsigned int R_NTC = TempConverBattThermistor(temp); long long Vin = 0; long long V_IR_comp = 0; /*int vbif28 = pmic_get_auxadc_value(AUXADC_LIST_VBIF);*/ int vbif28 = gm.rbat.rbat_pull_up_volt; static int fg_current_temp; static bool fg_current_state; int fg_r_value = fg_cust_data.com_r_fg_value; int fg_meter_res_value = 0; if (gm.no_bat_temp_compensate == 0) fg_meter_res_value = fg_cust_data.com_fg_meter_resistance; else fg_meter_res_value = 0; #ifdef RBAT_PULL_UP_VOLT_BY_BIF vbif28 = pmic_get_vbif28_volt(); #endif Vin = (long long)R_NTC * vbif28 * 10; /* 0.1 mV */ #if defined(__LP64__) || defined(_LP64) do_div(Vin, (R_NTC + gm.rbat.rbat_pull_up_r)); #else Vin = div_s64(Vin, (R_NTC + gm.rbat.rbat_pull_up_r)); #endif if (update == true) fg_current_state = gauge_get_current(&fg_current_temp); if (fg_current_state == true) { V_IR_comp = Vin; V_IR_comp += ((fg_current_temp * (fg_meter_res_value + fg_r_value)) / 10000); } else { V_IR_comp = Vin; V_IR_comp -= ((fg_current_temp * (fg_meter_res_value + fg_r_value)) / 10000); } bm_notice("[%s] temp %d R_NTC %d V(%lld %lld) I %d CHG %d\n", __func__, temp, R_NTC, Vin, V_IR_comp, fg_current_temp, fg_current_state); return (unsigned int) V_IR_comp; } int BattVoltToTemp(int dwVolt, int volt_cali) { long long TRes_temp; long long TRes; int sBaTTMP = -100; int vbif28 = gm.rbat.rbat_pull_up_volt; int delta_v; TRes_temp = (gm.rbat.rbat_pull_up_r * (long long) dwVolt); #ifdef RBAT_PULL_UP_VOLT_BY_BIF vbif28 = pmic_get_vbif28_volt() + volt_cali; delta_v = abs(vbif28 - dwVolt); if (delta_v == 0) delta_v = 1; #if defined(__LP64__) || defined(_LP64) do_div(TRes_temp, delta_v); #else TRes_temp = div_s64(TRes_temp, delta_v); #endif if (vbif28 > 3000 || vbif28 < 2500) bm_err( "[RBAT_PULL_UP_VOLT_BY_BIF] vbif28:%d\n", pmic_get_vbif28_volt()); #else delta_v = abs(gm.rbat.rbat_pull_up_volt - dwVolt); if (delta_v == 0) delta_v = 1; #if defined(__LP64__) || defined(_LP64) do_div(TRes_temp, delta_v); #else TRes_temp = div_s64(TRes_temp, delta_v); #endif #endif #ifdef RBAT_PULL_DOWN_R TRes = (TRes_temp * RBAT_PULL_DOWN_R); #if defined(__LP64__) || defined(_LP64) do_div(TRes, abs(RBAT_PULL_DOWN_R - TRes_temp)); #else TRes_temp = div_s64(TRes, abs(RBAT_PULL_DOWN_R - TRes_temp)); #endif #else TRes = TRes_temp; #endif /* convert register to temperature */ if (!pmic_is_bif_exist()) sBaTTMP = BattThermistorConverTemp((int)TRes); else sBaTTMP = BattThermistorConverTemp((int)TRes - gm.rbat.bif_ntc_r); bm_notice( "[%s] %d %d %d %d\n", __func__, dwVolt, gm.rbat.rbat_pull_up_r, vbif28, volt_cali); return sBaTTMP; } #if defined(CONFIG_BATTERY_SAMSUNG) void force_get_tbat_adc(void) { int bat_temperature_volt = 0; int fg_r_value = 0; int fg_meter_res_value = 0; int fg_current_temp = 0; bool fg_current_state = false; int bat_temperature_volt_temp = 0; int vol_cali = 0; /* Get V_BAT_Temperature */ bat_temperature_volt = 2; bat_temperature_volt = pmic_get_v_bat_temp(); if (bat_temperature_volt != 0) { fg_r_value = fg_cust_data.com_r_fg_value; if (gm.no_bat_temp_compensate == 0) fg_meter_res_value = fg_cust_data.com_fg_meter_resistance; else fg_meter_res_value = 0; gauge_dev_get_current( gm.gdev, &fg_current_state, &fg_current_temp); fg_current_temp = fg_current_temp / 10; if (fg_current_state == true) { bat_temperature_volt_temp = bat_temperature_volt; bat_temperature_volt = bat_temperature_volt - ((fg_current_temp * (fg_meter_res_value + fg_r_value)) / 10000); vol_cali = -((fg_current_temp * (fg_meter_res_value + fg_r_value)) / 10000); } else { bat_temperature_volt_temp = bat_temperature_volt; bat_temperature_volt = bat_temperature_volt + ((fg_current_temp * (fg_meter_res_value + fg_r_value)) / 10000); vol_cali = ((fg_current_temp * (fg_meter_res_value + fg_r_value)) / 10000); } } pr_info("%s: %d,%d,%d,%d,%d r:%d %d %d\n", __func__, bat_temperature_volt_temp, bat_temperature_volt, fg_current_state, fg_current_temp, fg_r_value, fg_meter_res_value, fg_r_value, gm.no_bat_temp_compensate); gm.tbat_adc = bat_temperature_volt; } #endif int force_get_tbat_internal(bool update) { int bat_temperature_volt = 0; int bat_temperature_val = 0; static int pre_bat_temperature_val = -1; int fg_r_value = 0; int fg_meter_res_value = 0; int fg_current_temp = 0; bool fg_current_state = false; int bat_temperature_volt_temp = 0; int vol_cali = 0; static int pre_bat_temperature_volt_temp, pre_bat_temperature_volt; static int pre_fg_current_temp; static int pre_fg_current_state; static int pre_fg_r_value; static int pre_bat_temperature_val2; static struct timespec pre_time; struct timespec ctime, dtime; if (is_battery_init_done() == false) { gm.tbat_precise = 250; #if defined(CONFIG_BATTERY_SAMSUNG) pr_info("[%s] fixed T=25\n", __func__); #endif return 25; } if (gm.fixed_bat_tmp != 0xffff) { gm.tbat_precise = gm.fixed_bat_tmp * 10; #if defined(CONFIG_BATTERY_SAMSUNG) pr_info("[%s] fixed T=gm.fixed_bat_tmp: %d\n", __func__, gm.tbat_precise); #endif return gm.fixed_bat_tmp; } if (get_ec()->fixed_temp_en) { gm.tbat_precise = get_ec()->fixed_temp_value * 10; #if defined(CONFIG_BATTERY_SAMSUNG) pr_info("[%s] fixed T=get_ec()->fixed_temp_value: %d\n", __func__, gm.tbat_precise); #endif return get_ec()->fixed_temp_value; } if (update == true || pre_bat_temperature_val == -1) { /* Get V_BAT_Temperature */ bat_temperature_volt = 2; bat_temperature_volt = pmic_get_v_bat_temp(); if (bat_temperature_volt != 0) { fg_r_value = fg_cust_data.com_r_fg_value; if (gm.no_bat_temp_compensate == 0) fg_meter_res_value = fg_cust_data.com_fg_meter_resistance; else fg_meter_res_value = 0; gauge_dev_get_current( gm.gdev, &fg_current_state, &fg_current_temp); fg_current_temp = fg_current_temp / 10; if (fg_current_state == true) { bat_temperature_volt_temp = bat_temperature_volt; bat_temperature_volt = bat_temperature_volt - ((fg_current_temp * (fg_meter_res_value + fg_r_value)) / 10000); vol_cali = -((fg_current_temp * (fg_meter_res_value + fg_r_value)) / 10000); } else { bat_temperature_volt_temp = bat_temperature_volt; bat_temperature_volt = bat_temperature_volt + ((fg_current_temp * (fg_meter_res_value + fg_r_value)) / 10000); vol_cali = ((fg_current_temp * (fg_meter_res_value + fg_r_value)) / 10000); } bat_temperature_val = BattVoltToTemp(bat_temperature_volt, vol_cali); } #ifdef CONFIG_MTK_BIF_SUPPORT /* CHARGING_CMD_GET_BIF_TBAT need fix */ #endif bm_notice("[force_get_tbat] %d,%d,%d,%d,%d,%d r:%d %d %d\n", bat_temperature_volt_temp, bat_temperature_volt, fg_current_state, fg_current_temp, fg_r_value, bat_temperature_val, fg_meter_res_value, fg_r_value, gm.no_bat_temp_compensate); if (pre_bat_temperature_val2 == 0) { pre_bat_temperature_volt_temp = bat_temperature_volt_temp; pre_bat_temperature_volt = bat_temperature_volt; pre_fg_current_temp = fg_current_temp; pre_fg_current_state = fg_current_state; pre_fg_r_value = fg_r_value; pre_bat_temperature_val2 = bat_temperature_val; get_monotonic_boottime(&pre_time); } else { get_monotonic_boottime(&ctime); dtime = timespec_sub(ctime, pre_time); if (((dtime.tv_sec <= 20) && (abs(pre_bat_temperature_val2 - bat_temperature_val) >= 50)) || bat_temperature_val >= 580) { bm_err( "[force_get_tbat][err] current:%d,%d,%d,%d,%d,%d pre:%d,%d,%d,%d,%d,%d\n", bat_temperature_volt_temp, bat_temperature_volt, fg_current_state, fg_current_temp, fg_r_value, bat_temperature_val, pre_bat_temperature_volt_temp, pre_bat_temperature_volt, pre_fg_current_state, pre_fg_current_temp, pre_fg_r_value, pre_bat_temperature_val2); /*pmic_auxadc_debug(1);*/ WARN_ON_ONCE(1); } pre_bat_temperature_volt_temp = bat_temperature_volt_temp; pre_bat_temperature_volt = bat_temperature_volt; pre_fg_current_temp = fg_current_temp; pre_fg_current_state = fg_current_state; pre_fg_r_value = fg_r_value; pre_bat_temperature_val2 = bat_temperature_val; pre_time = ctime; bm_trace( "[force_get_tbat] current:%d,%d,%d,%d,%d,%d pre:%d,%d,%d,%d,%d,%d time:%d\n", bat_temperature_volt_temp, bat_temperature_volt, fg_current_state, fg_current_temp, fg_r_value, bat_temperature_val, pre_bat_temperature_volt_temp, pre_bat_temperature_volt, pre_fg_current_state, pre_fg_current_temp, pre_fg_r_value, pre_bat_temperature_val2, (int)dtime.tv_sec); } } else { bat_temperature_val = pre_bat_temperature_val; } gm.tbat_precise = bat_temperature_val; return bat_temperature_val / 10; } int force_get_tbat(bool update) { int bat_temperature_val = 0; int counts = 0; if (is_fg_disabled()) { bm_debug("[%s] fixed TBAT=25 t\n", __func__); gm.tbat_precise = 250; return 25; } #if defined(FIXED_TBAT_25) bm_debug("[%s] fixed TBAT=25 t\n", __func__); gm.tbat_precise = 250; return 25; #else bat_temperature_val = force_get_tbat_internal(update); while (counts < 5 && bat_temperature_val >= 60) { bm_err("[%s]over60 count=%d, bat_temp=%d\n", __func__, counts, bat_temperature_val); bat_temperature_val = force_get_tbat_internal(true); counts++; } if (bat_temperature_val <= BATTERY_TMP_TO_DISABLE_GM30 && gm.disableGM30 == false) { bm_err( "battery temperature is too low %d and disable GM3.0\n", bat_temperature_val); disable_fg(); if (gm.disableGM30 == true) battery_main.BAT_CAPACITY = 50; battery_update(&battery_main); } if (bat_temperature_val <= BATTERY_TMP_TO_DISABLE_NAFG) { int fgv; fgv = gauge_get_hw_version(); if (fgv >= GAUGE_HW_V1000 && fgv < GAUGE_HW_V2000) { en_intr_VBATON_UNDET(0); } gm.ntc_disable_nafg = true; bm_err("[%s] ntc_disable_nafg %d %d\n", __func__, bat_temperature_val, DEFAULT_BATTERY_TMP_WHEN_DISABLE_NAFG); gm.tbat_precise = DEFAULT_BATTERY_TMP_WHEN_DISABLE_NAFG * 10; return DEFAULT_BATTERY_TMP_WHEN_DISABLE_NAFG; } gm.ntc_disable_nafg = false; bm_debug("[%s] t:%d precise:%d\n", __func__, bat_temperature_val, gm.tbat_precise); return bat_temperature_val; #endif } unsigned int battery_meter_get_fg_time(void) { unsigned int time = 0; gauge_dev_get_time(gm.gdev, &time); return time; } unsigned int battery_meter_enable_time_interrupt(unsigned int sec) { return gauge_dev_enable_time_interrupt(gm.gdev, sec); } /* ============================================================ */ /* Internal function */ /* ============================================================ */ void update_fg_dbg_tool_value(void) { /* Todo: backup variables */ } static void nl_send_to_user(u32 pid, int seq, struct fgd_nl_msg_t *reply_msg) { struct sk_buff *skb; struct nlmsghdr *nlh; /* int size=sizeof(struct fgd_nl_msg_t); */ int size = reply_msg->fgd_data_len + FGD_NL_MSG_T_HDR_LEN; int len = NLMSG_SPACE(size); void *data; int ret; reply_msg->identity = FGD_NL_MAGIC; if (in_interrupt()) skb = alloc_skb(len, GFP_ATOMIC); else skb = alloc_skb(len, GFP_KERNEL); if (!skb) return; nlh = nlmsg_put(skb, pid, seq, 0, size, 0); data = NLMSG_DATA(nlh); memcpy(data, reply_msg, size); NETLINK_CB(skb).portid = 0; /* from kernel */ NETLINK_CB(skb).dst_group = 0; /* unicast */ ret = netlink_unicast(gm.daemo_nl_sk, skb, pid, MSG_DONTWAIT); if (ret < 0) { bm_err("[Netlink] send failed %d\n", ret); return; } /*bm_debug("[Netlink] reply_user: netlink_unicast- ret=%d\n", ret); */ } static void nl_data_handler(struct sk_buff *skb) { u32 pid; kuid_t uid; int seq; void *data; struct nlmsghdr *nlh; struct fgd_nl_msg_t *fgd_msg, *fgd_ret_msg; int size = 0; nlh = (struct nlmsghdr *)skb->data; pid = NETLINK_CREDS(skb)->pid; uid = NETLINK_CREDS(skb)->uid; seq = nlh->nlmsg_seq; data = NLMSG_DATA(nlh); fgd_msg = (struct fgd_nl_msg_t *)data; if (IS_ENABLED(CONFIG_POWER_EXT) || gm.disable_mtkbattery || IS_ENABLED(CONFIG_MTK_DISABLE_GAUGE)) { bm_err("GM3 disable, nl handler rev data\n"); return; } if (fgd_msg->identity != FGD_NL_MAGIC) { bm_err("[FGERR]not correct MTKFG netlink packet!%d\n", fgd_msg->identity); return; } if (gm.g_fgd_pid != pid && fgd_msg->fgd_cmd > FG_DAEMON_CMD_SET_DAEMON_PID) { bm_err("drop rev netlink pid:%d:%d cmd:%d:%d\n", pid, gm.g_fgd_pid, fgd_msg->fgd_cmd, FG_DAEMON_CMD_SET_DAEMON_PID); return; } size = fgd_msg->fgd_ret_data_len + FGD_NL_MSG_T_HDR_LEN; if (size > (PAGE_SIZE << 1)) fgd_ret_msg = vmalloc(size); else { if (in_interrupt()) fgd_ret_msg = kmalloc(size, GFP_ATOMIC); else fgd_ret_msg = kmalloc(size, GFP_KERNEL); } if (fgd_ret_msg == NULL) { if (size > PAGE_SIZE) fgd_ret_msg = vmalloc(size); if (fgd_ret_msg == NULL) return; } memset(fgd_ret_msg, 0, size); bmd_ctrl_cmd_from_user(data, fgd_ret_msg); nl_send_to_user(pid, seq, fgd_ret_msg); kvfree(fgd_ret_msg); } int wakeup_fg_algo(unsigned int flow_state) { update_fg_dbg_tool_value(); if (gm.disableGM30) { bm_err("FG daemon is disabled\n"); return -1; } if (is_recovery_mode()) { wakeup_fg_algo_recovery(flow_state); return 0; } zcv_filter_add(&gm.zcvf); zcv_filter_dump(&gm.zcvf); zcv_check(&gm.zcvf); gm3_log_notify(flow_state); if (gm.g_fgd_pid != 0) { struct fgd_nl_msg_t *fgd_msg; int size = FGD_NL_MSG_T_HDR_LEN + sizeof(flow_state); if (size > (PAGE_SIZE << 1)) fgd_msg = vmalloc(size); else { if (in_interrupt()) fgd_msg = kmalloc(size, GFP_ATOMIC); else fgd_msg = kmalloc(size, GFP_KERNEL); } if (fgd_msg == NULL) { if (size > PAGE_SIZE) fgd_msg = vmalloc(size); if (fgd_msg == NULL) return -1; } bm_debug("[%s] malloc size=%d pid=%d cmd:%d\n", __func__, size, gm.g_fgd_pid, flow_state); memset(fgd_msg, 0, size); fgd_msg->fgd_cmd = FG_DAEMON_CMD_NOTIFY_DAEMON; memcpy(fgd_msg->fgd_data, &flow_state, sizeof(flow_state)); fgd_msg->fgd_data_len += sizeof(flow_state); nl_send_to_user(gm.g_fgd_pid, 0, fgd_msg); kvfree(fgd_msg); return 0; } else { return -1; } } int wakeup_fg_algo_cmd(unsigned int flow_state, int cmd, int para1) { update_fg_dbg_tool_value(); if (gm.disableGM30) { bm_err("FG daemon is disabled\n"); return -1; } if (is_recovery_mode()) { wakeup_fg_algo_recovery(flow_state); return 0; } gm3_log_notify(flow_state); if (gm.g_fgd_pid != 0) { struct fgd_nl_msg_t *fgd_msg; int size = FGD_NL_MSG_T_HDR_LEN + sizeof(flow_state); if (size > (PAGE_SIZE << 1)) fgd_msg = vmalloc(size); else { if (in_interrupt()) fgd_msg = kmalloc(size, GFP_ATOMIC); else fgd_msg = kmalloc(size, GFP_KERNEL); } if (fgd_msg == NULL) { if (size > PAGE_SIZE) fgd_msg = vmalloc(size); if (fgd_msg == NULL) return -1; } bm_debug( "[wakeup_fg_algo] malloc size=%d pid=%d cmd:%d\n", size, gm.g_fgd_pid, flow_state); memset(fgd_msg, 0, size); fgd_msg->fgd_cmd = FG_DAEMON_CMD_NOTIFY_DAEMON; fgd_msg->fgd_subcmd = cmd; fgd_msg->fgd_subcmd_para1 = para1; memcpy(fgd_msg->fgd_data, &flow_state, sizeof(flow_state)); fgd_msg->fgd_data_len += sizeof(flow_state); nl_send_to_user(gm.g_fgd_pid, 0, fgd_msg); kvfree(fgd_msg); return 0; } else { return -1; } } int wakeup_fg_algo_atomic(unsigned int flow_state) { update_fg_dbg_tool_value(); if (gm.disableGM30) { bm_err("FG daemon is disabled\n"); return -1; } if (is_recovery_mode()) { wakeup_fg_algo_recovery(flow_state); return 0; } gm3_log_notify(flow_state); if (gm.g_fgd_pid != 0) { struct fgd_nl_msg_t *fgd_msg; int size = FGD_NL_MSG_T_HDR_LEN + sizeof(flow_state); if (in_interrupt()) fgd_msg = kmalloc(size, GFP_ATOMIC); else fgd_msg = kmalloc(size, GFP_KERNEL); if (!fgd_msg) { /* bm_err("Error: wakeup_fg_algo() vmalloc fail!!!\n"); */ return -1; } bm_debug( "[wakeup_fg_algo] malloc size=%d pid=%d cmd:%d\n", size, gm.g_fgd_pid, flow_state); memset(fgd_msg, 0, size); fgd_msg->fgd_cmd = FG_DAEMON_CMD_NOTIFY_DAEMON; memcpy(fgd_msg->fgd_data, &flow_state, sizeof(flow_state)); fgd_msg->fgd_data_len += sizeof(flow_state); nl_send_to_user(gm.g_fgd_pid, 0, fgd_msg); kfree(fgd_msg); return 0; } else { return -1; } } int fg_get_battery_temperature_for_zcv(void) { return battery_main.BAT_batt_temp; } int battery_get_charger_zcv(void) { u32 zcv = 0; charger_manager_get_zcv(gm.pbat_consumer, MAIN_CHARGER, &zcv); return zcv; } void battery_set_charger_constant_voltage(u32 cv) { charger_manager_set_constant_voltage(gm.pbat_consumer, MAIN_CHARGER, cv); } void fg_ocv_query_soc(int ocv) { if (ocv > 50000 || ocv <= 0) return; gm.algo_req_ocv = ocv; wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_REQ_ALGO_DATA, ocv); bm_trace("[%s] ocv:%d\n", __func__, ocv); } void fg_test_ag_cmd(int cmd) { wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_AG_LOG_TEST, cmd); bm_err("[%s]FG_KERNEL_CMD_AG_LOG_TEST:%d\n", __func__, cmd); } void exec_BAT_EC(int cmd, int param) { int i; bm_err("exe_BAT_EC cmd %d, param %d\n", cmd, param); switch (cmd) { case 101: { /* Force Temperature, force_get_tbat*/ if (param == 0xff) { get_ec()->fixed_temp_en = 0; get_ec()->fixed_temp_value = 25; bm_err("exe_BAT_EC cmd %d, param %d, disable\n", cmd, param); } else { get_ec()->fixed_temp_en = 1; if (param >= 100) get_ec()->fixed_temp_value = 0 - (param - 100); else get_ec()->fixed_temp_value = param; bm_err("exe_BAT_EC cmd %d, param %d, enable\n", cmd, param); } } break; case 102: { /* force PTIM RAC */ if (param == 0xff) { get_ec()->debug_rac_en = 0; get_ec()->debug_rac_value = 0; bm_err("exe_BAT_EC cmd %d, param %d, disable\n", cmd, param); } else { get_ec()->debug_rac_en = 1; get_ec()->debug_rac_value = param; bm_err("exe_BAT_EC cmd %d, param %d, enable\n", cmd, param); } } break; case 103: { /* force PTIM V */ if (param == 0xff) { get_ec()->debug_ptim_v_en = 0; get_ec()->debug_ptim_v_value = 0; bm_err("exe_BAT_EC cmd %d, param %d, disable\n", cmd, param); } else { get_ec()->debug_ptim_v_en = 1; get_ec()->debug_ptim_v_value = param; bm_err("exe_BAT_EC cmd %d, param %d, enable\n", cmd, param); } } break; case 104: { /* force PTIM R_current */ if (param == 0xff) { get_ec()->debug_ptim_r_en = 0; get_ec()->debug_ptim_r_value = 0; bm_err("exe_BAT_EC cmd %d, param %d, disable\n", cmd, param); } else { get_ec()->debug_ptim_r_en = 1; get_ec()->debug_ptim_r_value = param; bm_err("exe_BAT_EC cmd %d, param %d, enable\n", cmd, param); } } break; case 105: { /* force interrupt trigger */ switch (param) { case 1: { wakeup_fg_algo(FG_INTR_TIMER_UPDATE); } break; case 4096: { wakeup_fg_algo(FG_INTR_NAG_C_DLTV); } break; case 8192: { wakeup_fg_algo(FG_INTR_FG_ZCV); } break; case 32768: { wakeup_fg_algo(FG_INTR_RESET_NVRAM); } break; case 65536: { wakeup_fg_algo(FG_INTR_BAT_PLUGOUT); } break; default: { } break; } bm_err( "exe_BAT_EC cmd %d, param %d, force interrupt\n", cmd, param); } break; case 106: { /* force FG Current */ if (param == 0xff) { get_ec()->debug_fg_curr_en = 0; get_ec()->debug_fg_curr_value = 0; bm_err( "exe_BAT_EC cmd %d, param %d, disable\n", cmd, param); } else { get_ec()->debug_fg_curr_en = 1; get_ec()->debug_fg_curr_value = param; bm_err( "exe_BAT_EC cmd %d, param %d, enable\n", cmd, param); } } break; case 107: { /* force Battery ID */ if (param == 0xff) { get_ec()->debug_bat_id_en = 0; get_ec()->debug_bat_id_value = 0; bm_err( "exe_BAT_EC cmd %d, param %d, disable\n", cmd, param); } else { get_ec()->debug_bat_id_en = 1; get_ec()->debug_bat_id_value = param; bm_err( "exe_BAT_EC cmd %d, param %d, enable\n", cmd, param); fg_custom_init_from_header(); } } break; case 108: { /* Set D0_C_CUST */ if (param == 0xff) { get_ec()->debug_d0_c_en = 0; get_ec()->debug_d0_c_value = 0; bm_err( "exe_BAT_EC cmd %d, param %d, disable\n", cmd, param); } else { get_ec()->debug_d0_c_en = 1; get_ec()->debug_d0_c_value = param; bm_err( "exe_BAT_EC cmd %d, param %d, enable\n", cmd, param); } } break; case 109: { /* Set D0_V_CUST */ if (param == 0xff) { get_ec()->debug_d0_v_en = 0; get_ec()->debug_d0_v_value = 0; bm_err( "exe_BAT_EC cmd %d, param %d, disable\n", cmd, param); } else { get_ec()->debug_d0_v_en = 1; get_ec()->debug_d0_v_value = param; bm_err( "exe_BAT_EC cmd %d, param %d, enable\n", cmd, param); } } break; case 110: { /* Set UISOC_CUST */ if (param == 0xff) { get_ec()->debug_uisoc_en = 0; get_ec()->debug_uisoc_value = 0; bm_err( "exe_BAT_EC cmd %d, param %d, disable\n", cmd, param); } else { get_ec()->debug_uisoc_en = 1; get_ec()->debug_uisoc_value = param; bm_err( "exe_BAT_EC cmd %d, param %d, enable\n", cmd, param); } } break; case 600: { fg_cust_data.aging_diff_max_threshold = param; bm_err( "exe_BAT_EC cmd %d, aging_diff_max_threshold:%d\n", cmd, param); } break; case 601: { fg_cust_data.aging_diff_max_level = param; bm_err( "exe_BAT_EC cmd %d, aging_diff_max_level:%d\n", cmd, param); } break; case 602: { fg_cust_data.aging_factor_t_min = param; bm_err( "exe_BAT_EC cmd %d, aging_factor_t_min:%d\n", cmd, param); } break; case 603: { fg_cust_data.cycle_diff = param; bm_err( "exe_BAT_EC cmd %d, cycle_diff:%d\n", cmd, param); } break; case 604: { fg_cust_data.aging_count_min = param; bm_err( "exe_BAT_EC cmd %d, aging_count_min:%d\n", cmd, param); } break; case 605: { fg_cust_data.default_score = param; bm_err( "exe_BAT_EC cmd %d, default_score:%d\n", cmd, param); } break; case 606: { fg_cust_data.default_score_quantity = param; bm_err( "exe_BAT_EC cmd %d, default_score_quantity:%d\n", cmd, param); } break; case 607: { fg_cust_data.fast_cycle_set = param; bm_err( "exe_BAT_EC cmd %d, fast_cycle_set:%d\n", cmd, param); } break; case 608: { fg_cust_data.level_max_change_bat = param; bm_err( "exe_BAT_EC cmd %d, level_max_change_bat:%d\n", cmd, param); } break; case 609: { fg_cust_data.diff_max_change_bat = param; bm_err( "exe_BAT_EC cmd %d, diff_max_change_bat:%d\n", cmd, param); } break; case 610: { fg_cust_data.aging_tracking_start = param; bm_err( "exe_BAT_EC cmd %d, aging_tracking_start:%d\n", cmd, param); } break; case 611: { fg_cust_data.max_aging_data = param; bm_err( "exe_BAT_EC cmd %d, max_aging_data:%d\n", cmd, param); } break; case 612: { fg_cust_data.max_fast_data = param; bm_err( "exe_BAT_EC cmd %d, max_fast_data:%d\n", cmd, param); } break; case 613: { fg_cust_data.fast_data_threshold_score = param; bm_err( "exe_BAT_EC cmd %d, fast_data_threshold_score:%d\n", cmd, param); } break; case 614: { bm_err( "exe_BAT_EC cmd %d,FG_KERNEL_CMD_AG_LOG_TEST=%d\n", cmd, param); fg_test_ag_cmd(99); } break; case 701: { fg_cust_data.pseudo1_en = param; bm_err( "exe_BAT_EC cmd %d, param %d, pseudo1_en\n", cmd, param); } break; case 702: { fg_cust_data.pseudo100_en = param; bm_err( "exe_BAT_EC cmd %d, param %d, pseudo100_en\n", cmd, param); } break; case 703: { fg_cust_data.qmax_sel = param; bm_err( "exe_BAT_EC cmd %d, param %d, qmax_sel\n", cmd, param); } break; case 704: { fg_cust_data.iboot_sel = param; bm_err( "exe_BAT_EC cmd %d, param %d, iboot_sel\n", cmd, param); } break; case 705: { for (i = 0; i < fg_table_cust_data.active_table_number; i++) { fg_table_cust_data.fg_profile[i].pmic_min_vol = param * UNIT_TRANS_10; } bm_err( "exe_BAT_EC cmd %d, param %d, pmic_min_vol\n", cmd, param); } break; case 706: { for (i = 0; i < fg_table_cust_data.active_table_number; i++) { fg_table_cust_data.fg_profile[i].pon_iboot = param * UNIT_TRANS_10; } bm_err("exe_BAT_EC cmd %d, param %d, poweron_system_iboot\n" , cmd, param * UNIT_TRANS_10); } break; case 707: { fg_cust_data.shutdown_system_iboot = param; bm_err( "exe_BAT_EC cmd %d, param %d, shutdown_system_iboot\n", cmd, param); } break; case 708: { fg_cust_data.com_fg_meter_resistance = param; bm_err( "exe_BAT_EC cmd %d, param %d, com_fg_meter_resistance\n", cmd, param); } break; case 709: { fg_cust_data.r_fg_value = param; bm_err( "exe_BAT_EC cmd %d, param %d, r_fg_value\n", cmd, param); } break; case 710: { fg_cust_data.q_max_sys_voltage = param; bm_err( "exe_BAT_EC cmd %d, param %d, q_max_sys_voltage\n", cmd, param); } break; case 711: { fg_cust_data.loading_1_en = param; bm_err( "exe_BAT_EC cmd %d, param %d, loading_1_en\n", cmd, param); } break; case 712: { fg_cust_data.loading_2_en = param; bm_err( "exe_BAT_EC cmd %d, param %d, loading_2_en\n", cmd, param); } break; case 713: { fg_cust_data.aging_temp_diff = param; bm_err( "exe_BAT_EC cmd %d, param %d, aging_temp_diff\n", cmd, param); } break; case 714: { fg_cust_data.aging1_load_soc = param; bm_err( "exe_BAT_EC cmd %d, param %d, aging1_load_soc\n", cmd, param); } break; case 715: { fg_cust_data.aging1_update_soc = param; bm_err( "exe_BAT_EC cmd %d, param %d, aging1_update_soc\n", cmd, param); } break; case 716: { fg_cust_data.aging_100_en = param; bm_err( "exe_BAT_EC cmd %d, param %d, aging_100_en\n", cmd, param); } break; case 717: { fg_table_cust_data.active_table_number = param; bm_err( "exe_BAT_EC cmd %d, param %d, additional_battery_table_en\n", cmd, param); } break; case 718: { fg_cust_data.d0_sel = param; bm_err( "exe_BAT_EC cmd %d, param %d, d0_sel\n", cmd, param); } break; case 719: { fg_cust_data.zcv_car_gap_percentage = param; bm_err( "exe_BAT_EC cmd %d, param %d, zcv_car_gap_percentage\n", cmd, param); } break; case 720: { fg_cust_data.multi_temp_gauge0 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.multi_temp_gauge0=%d\n", cmd, param); } break; case 721: { fg_custom_data_check(); bm_err("exe_BAT_EC cmd %d", cmd); } break; case 724: { fg_table_cust_data.fg_profile[0].pseudo100 = UNIT_TRANS_100 * param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pseudo100_t0=%d\n", cmd, param); } break; case 725: { fg_table_cust_data.fg_profile[1].pseudo100 = UNIT_TRANS_100 * param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pseudo100_t1=%d\n", cmd, param); } break; case 726: { fg_table_cust_data.fg_profile[2].pseudo100 = UNIT_TRANS_100 * param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pseudo100_t2=%d\n", cmd, param); } break; case 727: { fg_table_cust_data.fg_profile[3].pseudo100 = UNIT_TRANS_100 * param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pseudo100_t3=%d\n", cmd, param); } break; case 728: { fg_table_cust_data.fg_profile[4].pseudo100 = UNIT_TRANS_100 * param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pseudo100_t4=%d\n", cmd, param); } break; case 729: { fg_cust_data.keep_100_percent = UNIT_TRANS_100 * param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.keep_100_percent=%d\n", cmd, param); } break; case 730: { fg_cust_data.ui_full_limit_en = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.ui_full_limit_en=%d\n", cmd, param); } break; case 731: { fg_cust_data.ui_full_limit_soc0 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.ui_full_limit_soc0=%d\n", cmd, param); } break; case 732: { fg_cust_data.ui_full_limit_ith0 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.ui_full_limit_ith0=%d\n", cmd, param); } break; case 733: { fg_cust_data.ui_full_limit_soc1 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.ui_full_limit_soc1=%d\n", cmd, param); } break; case 734: { fg_cust_data.ui_full_limit_ith1 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.ui_full_limit_ith1=%d\n", cmd, param); } break; case 735: { fg_cust_data.ui_full_limit_soc2 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.ui_full_limit_soc2=%d\n", cmd, param); } break; case 736: { fg_cust_data.ui_full_limit_ith2 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.ui_full_limit_ith2=%d\n", cmd, param); } break; case 737: { fg_cust_data.ui_full_limit_soc3 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.ui_full_limit_soc3=%d\n", cmd, param); } break; case 738: { fg_cust_data.ui_full_limit_ith3 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.ui_full_limit_ith3=%d\n", cmd, param); } break; case 739: { fg_cust_data.ui_full_limit_soc4 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.ui_full_limit_soc4=%d\n", cmd, param); } break; case 740: { fg_cust_data.ui_full_limit_ith4 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.ui_full_limit_ith4=%d\n", cmd, param); } break; case 741: { fg_cust_data.ui_full_limit_time = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.ui_full_limit_time=%d\n", cmd, param); } break; case 743: { fg_table_cust_data.fg_profile[0].pmic_min_vol = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pmic_min_vol_t0=%d\n", cmd, param); } break; case 744: { fg_table_cust_data.fg_profile[1].pmic_min_vol = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pmic_min_vol_t1=%d\n", cmd, param); } break; case 745: { fg_table_cust_data.fg_profile[2].pmic_min_vol = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pmic_min_vol_t2=%d\n", cmd, param); } break; case 746: { fg_table_cust_data.fg_profile[3].pmic_min_vol = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pmic_min_vol_t3=%d\n", cmd, param); } break; case 747: { fg_table_cust_data.fg_profile[4].pmic_min_vol = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pmic_min_vol_t4=%d\n", cmd, param); } break; case 748: { fg_table_cust_data.fg_profile[0].pon_iboot = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pon_iboot_t0=%d\n", cmd, param); } break; case 749: { fg_table_cust_data.fg_profile[1].pon_iboot = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pon_iboot_t1=%d\n", cmd, param); } break; case 750: { fg_table_cust_data.fg_profile[2].pon_iboot = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pon_iboot_t2=%d\n", cmd, param); } break; case 751: { fg_table_cust_data.fg_profile[3].pon_iboot = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pon_iboot_t3=%d\n", cmd, param); } break; case 752: { fg_table_cust_data.fg_profile[4].pon_iboot = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.pon_iboot_t4=%d\n", cmd, param); } break; case 753: { fg_table_cust_data.fg_profile[0].qmax_sys_vol = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.qmax_sys_vol_t0=%d\n", cmd, param); } break; case 754: { fg_table_cust_data.fg_profile[1].qmax_sys_vol = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.qmax_sys_vol_t1=%d\n", cmd, param); } break; case 755: { fg_table_cust_data.fg_profile[2].qmax_sys_vol = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.qmax_sys_vol_t2=%d\n", cmd, param); } break; case 756: { fg_table_cust_data.fg_profile[3].qmax_sys_vol = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.qmax_sys_vol_t3=%d\n", cmd, param); } break; case 757: { fg_table_cust_data.fg_profile[4].qmax_sys_vol = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.qmax_sys_vol_t4=%d\n", cmd, param); } break; case 758: { fg_cust_data.nafg_ratio = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.nafg_ratio=%d\n", cmd, param); } break; case 759: { fg_cust_data.nafg_ratio_en = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.nafg_ratio_en=%d\n", cmd, param); } break; case 760: { fg_cust_data.nafg_ratio_tmp_thr = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.nafg_ratio_tmp_thr=%d\n", cmd, param); } break; case 761: { wakeup_fg_algo(FG_INTR_BAT_CYCLE); bm_err( "exe_BAT_EC cmd %d, bat_cycle intr\n", cmd); } break; case 762: { fg_cust_data.difference_fgc_fgv_th1 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.difference_fgc_fgv_th1=%d\n", cmd, param); } break; case 763: { fg_cust_data.difference_fgc_fgv_th2 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.difference_fgc_fgv_th1=%d\n", cmd, param); } break; case 764: { fg_cust_data.difference_fgc_fgv_th3 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.difference_fgc_fgv_th3=%d\n", cmd, param); } break; case 765: { fg_cust_data.difference_fullocv_ith = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.difference_fullocv_ith=%d\n", cmd, param); } break; case 766: { fg_cust_data.difference_fgc_fgv_th_soc1 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.difference_fgc_fgv_th_soc1=%d\n", cmd, param); } break; case 767: { fg_cust_data.difference_fgc_fgv_th_soc2 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.difference_fgc_fgv_th_soc2=%d\n", cmd, param); } break; case 768: { fg_cust_data.embedded_sel = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.embedded_sel=%d\n", cmd, param); } break; case 769: { fg_cust_data.car_tune_value = param * UNIT_TRANS_10; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.car_tune_value=%d\n", cmd, param * UNIT_TRANS_10); } break; case 770: { fg_cust_data.shutdown_1_time = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.shutdown_1_time=%d\n", cmd, param); } break; case 771: { fg_cust_data.tnew_told_pon_diff = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.tnew_told_pon_diff=%d\n", cmd, param); } break; case 772: { fg_cust_data.tnew_told_pon_diff2 = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.tnew_told_pon_diff2=%d\n", cmd, param); } break; case 773: { fg_cust_data.swocv_oldocv_diff = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.swocv_oldocv_diff=%d\n", cmd, param); } break; case 774: { fg_cust_data.hwocv_oldocv_diff = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.hwocv_oldocv_diff=%d\n", cmd, param); } break; case 775: { fg_cust_data.hwocv_swocv_diff = param; bm_err( "exe_BAT_EC cmd %d, fg_cust_data.hwocv_swocv_diff=%d\n", cmd, param); } break; case 776: { get_ec()->debug_kill_daemontest = param; bm_err( "exe_BAT_EC cmd %d, debug_kill_daemontest=%d\n", cmd, param); } break; case 777: { fg_cust_data.swocv_oldocv_diff_emb = param; bm_err("exe_BAT_EC cmd %d, swocv_oldocv_diff_emb=%d\n", cmd, param); } break; case 778: { fg_cust_data.vir_oldocv_diff_emb_lt = param; bm_err( "exe_BAT_EC cmd %d, vir_oldocv_diff_emb_lt=%d\n", cmd, param); } break; case 779: { fg_cust_data.vir_oldocv_diff_emb_tmp = param; bm_err( "exe_BAT_EC cmd %d, vir_oldocv_diff_emb_tmp=%d\n", cmd, param); } break; case 780: { fg_cust_data.vir_oldocv_diff_emb = param; bm_err("exe_BAT_EC cmd %d, vir_oldocv_diff_emb=%d\n", cmd, param); } break; case 781: { get_ec()->debug_kill_daemontest = 1; fg_cust_data.dod_init_sel = 12; bm_err("exe_BAT_EC cmd %d,force goto dod_init12=%d\n", cmd, param); } break; case 782: { fg_cust_data.charge_pseudo_full_level = param; bm_err( "exe_BAT_EC cmd %d,fg_cust_data.charge_pseudo_full_level=%d\n", cmd, param); } break; case 783: { fg_cust_data.full_tracking_bat_int2_multiply = param; bm_err( "exe_BAT_EC cmd %d,fg_cust_data.full_tracking_bat_int2_multiply=%d\n", cmd, param); } break; case 784: { bm_err( "exe_BAT_EC cmd %d,DAEMON_PID=%d\n", cmd, gm.g_fgd_pid); } break; case 785: { gm.bat_cycle_thr = param; bm_err( "exe_BAT_EC cmd %d,thr=%d\n", cmd, gm.bat_cycle_thr); } break; case 786: { gm.bat_cycle_ncar = param; bm_err( "exe_BAT_EC cmd %d,thr=%d\n", cmd, gm.bat_cycle_ncar); } break; case 787: { fg_ocv_query_soc(param); bm_err( "exe_BAT_EC cmd %d,[fg_ocv_query_soc]ocv=%d\n", cmd, param); } break; case 788: { fg_cust_data.record_log = param; bm_err( "exe_BAT_EC cmd %d,record_log=%d\n", cmd, fg_cust_data.record_log); } break; case 789: { int zcv_avg_current = 0; zcv_avg_current = param; gauge_set_zcv_interrupt_en(0); gauge_dev_set_zcv_interrupt_threshold(gm.gdev, zcv_avg_current); gauge_set_zcv_interrupt_en(1); bm_err( "exe_BAT_EC cmd %d,zcv_avg_current =%d\n", cmd, param); } break; case 790: { bm_err( "exe_BAT_EC cmd %d,force DLPT shutdown", cmd); notify_fg_dlpt_sd(); } break; case 791: { gm.enable_tmp_intr_suspend = param; bm_err( "exe_BAT_EC cmd %d,gm.enable_tmp_intr_suspend =%d\n", cmd, param); } break; case 792: { wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_BUILD_SEL_BATTEMP, param); bm_err( "exe_BAT_EC cmd %d,req bat table temp =%d\n", cmd, param); } break; case 793: { wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_UPDATE_AVG_BATTEMP, param); bm_err( "exe_BAT_EC cmd %d,update mavg temp\n", cmd); } break; case 794: { wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_SAVE_DEBUG_PARAM, param); bm_err( "exe_BAT_EC cmd %d,FG_KERNEL_CMD_SAVE_DEBUG_PARAM\n", cmd); } break; case 795: { wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_REQ_CHANGE_AGING_DATA, param * 100); bm_err( "exe_BAT_EC cmd %d,change aging to=%d\n", cmd, param); } break; case 796: { bm_err( "exe_BAT_EC cmd %d,FG_KERNEL_CMD_AG_LOG_TEST=%d\n", cmd, param); wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_AG_LOG_TEST, param); } break; case 797: { gm.soc_decimal_rate = param; bm_err( "exe_BAT_EC cmd %d,soc_decimal_rate=%d\n", cmd, param); } break; case 798: { bm_err( "exe_BAT_EC cmd %d,FG_KERNEL_CMD_CHG_DECIMAL_RATE=%d\n", cmd, param); gm.soc_decimal_rate = param; wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_CHG_DECIMAL_RATE, param); } break; case 799: { bm_err( "exe_BAT_EC cmd %d, Send INTR_CHR_FULL to daemon, force_full =%d\n", cmd, param); gm.is_force_full = param; wakeup_fg_algo(FG_INTR_CHR_FULL); } break; case 800: { bm_err( "exe_BAT_EC cmd %d, charge_power_sel =%d\n", cmd, param); set_charge_power_sel(param); } break; case 801: { bm_err( "exe_BAT_EC cmd %d, charge_power_sel =%d\n", cmd, param); dump_pseudo100(param); } break; case 802: { fg_cust_data.zcv_com_vol_limit = param; bm_err( "exe_BAT_EC cmd %d,zcv_com_vol_limit =%d\n", cmd, param); } break; case 803: { fg_cust_data.sleep_current_avg = param; bm_err( "exe_BAT_EC cmd %d,sleep_current_avg =%d\n", cmd, param); } break; default: bm_err( "exe_BAT_EC cmd %d, param %d, default\n", cmd, param); break; } } static ssize_t show_FG_daemon_disable( struct device *dev, struct device_attribute *attr, char *buf) { bm_trace("[FG] show FG disable : %d\n", gm.disableGM30); return sprintf(buf, "%d\n", gm.disableGM30); } static ssize_t store_FG_daemon_disable( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { bm_err("[disable FG daemon]\n"); disable_fg(); if (gm.disableGM30 == true) battery_main.BAT_CAPACITY = 50; battery_update(&battery_main); return size; } static DEVICE_ATTR( FG_daemon_disable, 0664, show_FG_daemon_disable, store_FG_daemon_disable); static ssize_t show_FG_meter_resistance( struct device *dev, struct device_attribute *attr, char *buf) { bm_trace( "[FG] show com_fg_meter_resistance : %d\n", fg_cust_data.com_fg_meter_resistance); return sprintf(buf, "%d\n", fg_cust_data.com_fg_meter_resistance); } static ssize_t store_FG_meter_resistance( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; bm_err("[%s]\n", __func__); if (buf != NULL && size != 0) { bm_err("[%s] buf is %s\n", __func__, buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { bm_err( "[%s] val is %d ??\n", __func__, (int)val); val = 0; } else fg_cust_data.com_fg_meter_resistance = val; bm_err( "[%s] com FG_meter_resistance = %d\n", __func__, (int)val); } return size; } static DEVICE_ATTR( FG_meter_resistance, 0664, show_FG_meter_resistance, store_FG_meter_resistance); static ssize_t show_FG_nafg_disable( struct device *dev, struct device_attribute *attr, char *buf) { bm_trace("[FG] show nafg disable : %d\n", gm.cmd_disable_nafg); return sprintf(buf, "%d\n", gm.cmd_disable_nafg); } static ssize_t store_FG_nafg_disable( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; bm_err("[%s]\n", __func__); if (buf != NULL && size != 0) { bm_err("[%s] buf is %s\n", __func__, buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { bm_err( "[%s] val is %d ??\n", __func__, (int)val); val = 0; } if (val == 0) gm.cmd_disable_nafg = false; else gm.cmd_disable_nafg = true; wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_DISABLE_NAFG, val); bm_err( "[%s] FG_nafg_disable = %d\n", __func__, (int)val); } return size; } static DEVICE_ATTR( disable_nafg, 0664, show_FG_nafg_disable, store_FG_nafg_disable); static ssize_t show_FG_ntc_disable_nafg( struct device *dev, struct device_attribute *attr, char *buf) { bm_trace("[FG]%s: %d\n", __func__, gm.ntc_disable_nafg); return sprintf(buf, "%d\n", gm.ntc_disable_nafg); } static ssize_t store_FG_ntc_disable_nafg( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; bm_err("[%s]\n", __func__); if (buf != NULL && size != 0) { bm_err("[%s] buf is %s\n", __func__, buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { bm_err( "[%s] val is %d ??\n", __func__, (int)val); val = 0; } if (val == 0) { gm.ntc_disable_nafg = false; get_ec()->fixed_temp_en = 0; get_ec()->fixed_temp_value = 25; } else if (val == 1) { get_ec()->fixed_temp_en = 1; get_ec()->fixed_temp_value = BATTERY_TMP_TO_DISABLE_NAFG; gm.ntc_disable_nafg = true; wakeup_fg_algo(FG_INTR_NAG_C_DLTV); } bm_err( "[%s]val=%d, temp:%d %d, %d\n", __func__, (int)val, get_ec()->fixed_temp_en, get_ec()->fixed_temp_value, gm.ntc_disable_nafg); } return size; } static DEVICE_ATTR( ntc_disable_nafg, 0664, show_FG_ntc_disable_nafg, store_FG_ntc_disable_nafg); static ssize_t show_uisoc_update_type( struct device *dev, struct device_attribute *attr, char *buf) { bm_trace("[FG] %s : %d\n", __func__, fg_cust_data.uisoc_update_type); return sprintf(buf, "%d\n", fg_cust_data.uisoc_update_type); } static ssize_t store_uisoc_update_type( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; bm_err("[%s]\n", __func__); if (buf != NULL && size != 0) { bm_err("[%s] buf is %s\n", __func__, buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { bm_err( "[%s] val is %d ??\n", __func__, (int)val); val = 0; } if (val >= 0 && val <= 2) { fg_cust_data.uisoc_update_type = val; wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_UISOC_UPDATE_TYPE, val); bm_err( "[%s] type = %d\n", __func__, (int)val); } else bm_err( "[%s] invalid type:%d\n", __func__, (int)val); } return size; } static DEVICE_ATTR(uisoc_update_type, 0664, show_uisoc_update_type, store_uisoc_update_type); static ssize_t show_FG_daemon_log_level( struct device *dev, struct device_attribute *attr, char *buf) { static int loglevel_count; loglevel_count++; if (loglevel_count % 5 == 0) bm_err( "[FG] show FG_daemon_log_level : %d\n", fg_cust_data.daemon_log_level); return sprintf(buf, "%d\n", fg_cust_data.daemon_log_level); } static ssize_t store_FG_daemon_log_level( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; bm_err("[FG_daemon_log_level]\n"); if (buf != NULL && size != 0) { bm_err("[FG_daemon_log_level] buf is %s\n", buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { bm_err( "[FG_daemon_log_level] val is %d ??\n", (int)val); val = 0; } if (val < 10 && val >= 0) { fg_cust_data.daemon_log_level = val; wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_CHANG_LOGLEVEL, val ); gm.d_log_level = val; gm.log_level = val; } if (val >= 7) gauge_coulomb_set_log_level(3); else gauge_coulomb_set_log_level(0); bm_err( "[FG_daemon_log_level]fg_cust_data.daemon_log_level=%d\n", fg_cust_data.daemon_log_level); } return size; } static DEVICE_ATTR(FG_daemon_log_level, 0664, show_FG_daemon_log_level, store_FG_daemon_log_level); static ssize_t show_shutdown_cond_enable( struct device *dev, struct device_attribute *attr, char *buf) { bm_trace( "[FG] %s : %d\n", __func__, get_shutdown_cond_flag()); return sprintf(buf, "%d\n", get_shutdown_cond_flag()); } static ssize_t store_shutdown_cond_enable( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; bm_err("[%s]\n", __func__); if (buf != NULL && size != 0) { bm_err("[%s] buf is %s\n", __func__, buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { bm_err( "[%s] val is %d ??\n", __func__, (int)val); val = 0; } set_shutdown_cond_flag(val); bm_err( "[%s] shutdown_cond_enabled=%d\n", __func__, get_shutdown_cond_flag()); } return size; } static DEVICE_ATTR( shutdown_condition_enable, 0664, show_shutdown_cond_enable, store_shutdown_cond_enable); static ssize_t show_reset_battery_cycle( struct device *dev, struct device_attribute *attr, char *buf) { bm_trace("[FG] %s : %d\n", __func__, gm.is_reset_battery_cycle); return sprintf(buf, "%d\n", gm.is_reset_battery_cycle); } static ssize_t store_reset_battery_cycle( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; bm_err("[%s]\n", __func__); if (buf != NULL && size != 0) { bm_err("[%s] buf is %s\n", __func__, buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { bm_err( "[%s] val is %d ??\n", __func__, (int)val); val = 0; } if (val == 0) gm.is_reset_battery_cycle = false; else { gm.is_reset_battery_cycle = true; wakeup_fg_algo(FG_INTR_BAT_CYCLE); } bm_err( "%s=%d\n", __func__, gm.is_reset_battery_cycle); } return size; } static DEVICE_ATTR( reset_battery_cycle, 0664, show_reset_battery_cycle, store_reset_battery_cycle); static ssize_t show_reset_aging_factor( struct device *dev, struct device_attribute *attr, char *buf) { bm_trace("[FG] %s : %d\n", __func__, gm.is_reset_aging_factor); return sprintf(buf, "%d\n", gm.is_reset_aging_factor); } static ssize_t store_reset_aging_factor( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; bm_err("[%s]\n", __func__); if (buf != NULL && size != 0) { bm_err("[%s] buf is %s\n", __func__, buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { bm_err( "[%s] val is %d ??\n", __func__, (int)val); val = 0; } if (val == 0) gm.is_reset_aging_factor = false; else if (val == 1) { gm.is_reset_aging_factor = true; wakeup_fg_algo_cmd(FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_RESET_AGING_FACTOR, 0); } bm_err( "%s=%d\n", __func__, gm.is_reset_aging_factor); } return size; } static DEVICE_ATTR( reset_aging_factor, 0664, show_reset_aging_factor, store_reset_aging_factor); static ssize_t show_BAT_EC( struct device *dev, struct device_attribute *attr, char *buf) { bm_err("%s\n", __func__); return sprintf(buf, "%d:%d\n", gm.BAT_EC_cmd, gm.BAT_EC_param); } static ssize_t store_BAT_EC( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret1 = 0, ret2 = 0; char cmd_buf[4], param_buf[16]; bm_err("%s\n", __func__); cmd_buf[3] = '\0'; param_buf[15] = '\0'; if (size < 4 || size > 20) { bm_err("%s error, size mismatch\n", __func__); return -1; } if (buf != NULL && size != 0) { bm_err("buf is %s\n", buf); cmd_buf[0] = buf[0]; cmd_buf[1] = buf[1]; cmd_buf[2] = buf[2]; cmd_buf[3] = '\0'; if ((size - 4) > 0) { strncpy(param_buf, buf + 4, size - 4); param_buf[size - 4 - 1] = '\0'; bm_err("[FG_IT]cmd_buf %s, param_buf %s\n", cmd_buf, param_buf); ret2 = kstrtouint(param_buf, 10, &gm.BAT_EC_param); } ret1 = kstrtouint(cmd_buf, 10, &gm.BAT_EC_cmd); if (ret1 != 0 || ret2 != 0) { bm_err("ERROR! not valid number! %d %d\n", ret1, ret2); return -1; } bm_err("CMD is:%d, param:%d\n", gm.BAT_EC_cmd, gm.BAT_EC_param); } exec_BAT_EC(gm.BAT_EC_cmd, gm.BAT_EC_param); return size; } static DEVICE_ATTR(BAT_EC, 0664, show_BAT_EC, store_BAT_EC); static ssize_t show_BAT_HEALTH( struct device *dev, struct device_attribute *attr, char *buf) { bm_err("%s\n", __func__); return 0; } static ssize_t store_BAT_HEALTH( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { char copy_str[7], buf_str[350]; char *s = buf_str, *pch; /* char *ori = buf_str; */ int chr_size = 0; int i = 0, j = 0, count = 0, value[50]; bm_err("%s, size =%d, str=%s\n", __func__, size, buf); if (size < 90 || size > 350) { bm_err("%s error, size mismatch\n", __func__); return -1; } else { for (i = 0; i < strlen(buf); i++) { if (buf[i] == ',') j++; } if (j != 46) { bm_err("%s error, invalid input\n", __func__); return -1; } } strncpy(buf_str, buf, size); /* bm_err("%s, copy str=%s\n", __func__, buf_str); */ if (buf != NULL && size != 0) { pch = strchr(s, ','); while (pch != NULL) { memset(copy_str, 0, 7); copy_str[6] = '\0'; chr_size = pch - s; if (count == 0) strncpy(copy_str, s, chr_size); else strncpy(copy_str, s+1, chr_size-1); kstrtoint(copy_str, 10, &value[count]); /* bm_err("::%s::count:%d,%d\n", copy_str, count, value[count]); */ s = pch; pch = strchr(pch + 1, ','); count++; } } if (count == 46) { for (i = 0; i < 43; i++) gm.bh_data.data[i] = value[i]; for (i = 0; i < 3; i++) gm.bh_data.times[i].tv_sec = value[i+43]; bm_err("%s count=%d,serial=%d,source=%d,42:%d, value43:[%d, %ld],value45[%d %ld]\n", __func__, count, gm.bh_data.data[0], gm.bh_data.data[1], gm.bh_data.data[42], value[43], gm.bh_data.times[0].tv_sec, value[45], gm.bh_data.times[2].tv_sec); wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_SEND_BH_DATA, 0); mdelay(4); bm_err("%s wakeup DONE~~~\n", __func__); } else bm_err("%s count=%d, number not match\n", __func__, count); return size; } static DEVICE_ATTR(BAT_HEALTH, 0664, show_BAT_HEALTH, store_BAT_HEALTH); static ssize_t show_FG_Battery_CurrentConsumption( struct device *dev, struct device_attribute *attr, char *buf) { int ret_value = 8888; ret_value = battery_get_bat_avg_current(); bm_err("[EM] FG_Battery_CurrentConsumption : %d .1mA\n", ret_value); return sprintf(buf, "%d\n", ret_value); } static ssize_t store_FG_Battery_CurrentConsumption(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { bm_err("[EM] Not Support Write Function\n"); return size; } static DEVICE_ATTR( FG_Battery_CurrentConsumption, 0664, show_FG_Battery_CurrentConsumption, store_FG_Battery_CurrentConsumption); /* /////////////////////////////////////*/ /* // Create File For EM : Power_On_Voltage */ /* /////////////////////////////////////*/ static ssize_t show_Power_On_Voltage( struct device *dev, struct device_attribute *attr, char *buf) { int ret_value = 1; ret_value = 3450; bm_err("[EM] Power_On_Voltage : %d\n", ret_value); return sprintf(buf, "%u\n", ret_value); } static ssize_t store_Power_On_Voltage( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { bm_err("[EM] Not Support Write Function\n"); return size; } static DEVICE_ATTR( Power_On_Voltage, 0664, show_Power_On_Voltage, store_Power_On_Voltage); /* /////////////////////////////////////////// */ /* // Create File For EM : Power_Off_Voltage */ /* /////////////////////////////////////////// */ static ssize_t show_Power_Off_Voltage( struct device *dev, struct device_attribute *attr, char *buf) { int ret_value = 1; ret_value = 3400; bm_err("[EM] Power_Off_Voltage : %d\n", ret_value); return sprintf(buf, "%u\n", ret_value); } static ssize_t store_Power_Off_Voltage( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { bm_err("[EM] Not Support Write Function\n"); return size; } static DEVICE_ATTR( Power_Off_Voltage, 0664, show_Power_Off_Voltage, store_Power_Off_Voltage); static int battery_callback( struct notifier_block *nb, unsigned long event, void *v) { bm_err("%s:%ld\n", __func__, event); switch (event) { case CHARGER_NOTIFY_EOC: { /* CHARGING FULL */ #if !defined(CONFIG_BATTERY_SAMSUNG) /* Moved to use sec_battery.c set property */ notify_fg_chr_full(); #endif } break; case CHARGER_NOTIFY_START_CHARGING: { /* START CHARGING */ #if !defined(CONFIG_BATTERY_SAMSUNG) /* Moved to use sec_battery.c set property */ fg_sw_bat_cycle_accu(); battery_main.BAT_STATUS = POWER_SUPPLY_STATUS_CHARGING; battery_update(&battery_main); #endif } break; case CHARGER_NOTIFY_STOP_CHARGING: { /* STOP CHARGING */ #if !defined(CONFIG_BATTERY_SAMSUNG) /* Moved to use sec_battery.c set property */ fg_sw_bat_cycle_accu(); battery_main.BAT_STATUS = POWER_SUPPLY_STATUS_DISCHARGING; battery_update(&battery_main); #endif } break; case CHARGER_NOTIFY_ERROR: { /* charging enter error state */ battery_main.BAT_STATUS = POWER_SUPPLY_STATUS_NOT_CHARGING; battery_update(&battery_main); } break; case CHARGER_NOTIFY_NORMAL: { /* charging leave error state */ battery_main.BAT_STATUS = POWER_SUPPLY_STATUS_CHARGING; battery_update(&battery_main); } break; default: { } break; } return NOTIFY_DONE; } /********** adc_cdev*******************/ signed int battery_meter_meta_tool_cali_car_tune(int meta_current) { int cali_car_tune = 0; int ret = 0; if (meta_current == 0) return fg_cust_data.car_tune_value * 10; ret = gauge_dev_enable_car_tune_value_calibration( gm.gdev, meta_current, &cali_car_tune); return cali_car_tune; /* 1000 base */ } #if IS_ENABLED(CONFIG_COMPAT) static long compat_adc_cali_ioctl( struct file *filp, unsigned int cmd, unsigned long arg) { int adc_out_datas[2] = { 1, 1 }; bm_notice("%s 32bit IOCTL, cmd=0x%08x\n", __func__, cmd); if (!filp->f_op || !filp->f_op->unlocked_ioctl) { bm_err("%s file has no f_op or no f_op->unlocked_ioctl.\n", __func__); return -ENOTTY; } if (sizeof(arg) != sizeof(adc_out_datas)) return -EFAULT; switch (cmd) { case BAT_STATUS_READ: case ADC_CHANNEL_READ: case Get_META_BAT_VOL: case Get_META_BAT_SOC: case Get_META_BAT_CAR_TUNE_VALUE: case Set_META_BAT_CAR_TUNE_VALUE: case Set_BAT_DISABLE_NAFG: case Set_CARTUNE_TO_KERNEL: { bm_notice( "%s send to unlocked_ioctl cmd=0x%08x\n", __func__, cmd); return filp->f_op->unlocked_ioctl( filp, cmd, (unsigned long)compat_ptr(arg)); } break; default: bm_err("%s unknown IOCTL: 0x%08x, %d\n", __func__, cmd, adc_out_datas[0]); break; } return 0; } #endif static long adc_cali_ioctl( struct file *file, unsigned int cmd, unsigned long arg) { int *user_data_addr; int *naram_data_addr; int i = 0; int ret = 0; int adc_in_data[2] = { 1, 1 }; int adc_out_data[2] = { 1, 1 }; int temp_car_tune; int isdisNAFG = 0; bm_notice("%s enter\n", __func__); mutex_lock(&gm.fg_mutex); user_data_addr = (int *)arg; ret = copy_from_user(adc_in_data, user_data_addr, sizeof(adc_in_data)); if (adc_in_data[1] < 0) { bm_err("%s unknown data: %d\n", __func__, adc_in_data[1]); mutex_unlock(&gm.fg_mutex); return -EFAULT; } switch (cmd) { case TEST_ADC_CALI_PRINT: g_ADC_Cali = false; break; case SET_ADC_CALI_Slop: naram_data_addr = (int *)arg; ret = copy_from_user(adc_cali_slop, naram_data_addr, 36); g_ADC_Cali = false; /* Protection */ for (i = 0; i < 14; i++) { if ((*(adc_cali_slop + i) == 0) || (*(adc_cali_slop + i) == 1)) *(adc_cali_slop + i) = 1000; } for (i = 0; i < 14; i++) bm_notice("adc_cali_slop[%d] = %d\n", i, *(adc_cali_slop + i)); bm_notice("**** unlocked_ioctl : SET_ADC_CALI_Slop Done!\n"); break; case SET_ADC_CALI_Offset: naram_data_addr = (int *)arg; ret = copy_from_user(adc_cali_offset, naram_data_addr, 36); g_ADC_Cali = false; for (i = 0; i < 14; i++) bm_notice("adc_cali_offset[%d] = %d\n", i, *(adc_cali_offset + i)); bm_notice("**** unlocked_ioctl : SET_ADC_CALI_Offset Done!\n"); break; case SET_ADC_CALI_Cal: naram_data_addr = (int *)arg; ret = copy_from_user(adc_cali_cal, naram_data_addr, 4); g_ADC_Cali = true; if (adc_cali_cal[0] == 1) g_ADC_Cali = true; else g_ADC_Cali = false; for (i = 0; i < 1; i++) bm_notice("adc_cali_cal[%d] = %d\n", i, *(adc_cali_cal + i)); bm_notice("**** unlocked_ioctl : SET_ADC_CALI_Cal Done!\n"); break; case ADC_CHANNEL_READ: /* g_ADC_Cali = KAL_FALSE; *//* 20100508 Infinity */ if (adc_in_data[0] == 0) { /* I_SENSE */ adc_out_data[0] = battery_get_bat_voltage() * adc_in_data[1]; } else if (adc_in_data[0] == 1) { /* BAT_SENSE */ adc_out_data[0] = battery_get_bat_voltage() * adc_in_data[1]; } else if (adc_in_data[0] == 3) { /* V_Charger */ adc_out_data[0] = battery_get_vbus() * adc_in_data[1]; /* adc_out_data[0] = adc_out_data[0] / 100; */ } else if (adc_in_data[0] == 30) { /* V_Bat_temp magic number */ adc_out_data[0] = battery_get_bat_temperature() * adc_in_data[1]; } else if (adc_in_data[0] == 66) adc_out_data[0] = (battery_get_bat_current()) / 10; else { bm_notice("unknown channel(%d,%d)\n", adc_in_data[0], adc_in_data[1]); } if (adc_out_data[0] < 0) adc_out_data[1] = 1; /* failed */ else adc_out_data[1] = 0; /* success */ if (adc_in_data[0] == 30) adc_out_data[1] = 0; /* success */ if (adc_in_data[0] == 66) adc_out_data[1] = 0; /* success */ ret = copy_to_user(user_data_addr, adc_out_data, 8); bm_notice( "**** unlocked_ioctl : Channel %d * %d times = %d\n", adc_in_data[0], adc_in_data[1], adc_out_data[0]); break; case BAT_STATUS_READ: user_data_addr = (int *)arg; ret = copy_from_user(battery_in_data, user_data_addr, 4); /* [0] is_CAL */ if (g_ADC_Cali) battery_out_data[0] = 1; else battery_out_data[0] = 0; ret = copy_to_user(user_data_addr, battery_out_data, 4); bm_notice( "unlocked_ioctl : CAL:%d\n", battery_out_data[0]); break; #if 0 case Set_Charger_Current: /* For Factory Mode */ user_data_addr = (int *)arg; ret = copy_from_user(charging_level_data, user_data_addr, 4); g_ftm_battery_flag = KAL_TRUE; if (charging_level_data[0] == 0) charging_level_data[0] = CHARGE_CURRENT_70_00_MA; else if (charging_level_data[0] == 1) charging_level_data[0] = CHARGE_CURRENT_200_00_MA; else if (charging_level_data[0] == 2) charging_level_data[0] = CHARGE_CURRENT_400_00_MA; else if (charging_level_data[0] == 3) charging_level_data[0] = CHARGE_CURRENT_450_00_MA; else if (charging_level_data[0] == 4) charging_level_data[0] = CHARGE_CURRENT_550_00_MA; else if (charging_level_data[0] == 5) charging_level_data[0] = CHARGE_CURRENT_650_00_MA; else if (charging_level_data[0] == 6) charging_level_data[0] = CHARGE_CURRENT_700_00_MA; else if (charging_level_data[0] == 7) charging_level_data[0] = CHARGE_CURRENT_800_00_MA; else if (charging_level_data[0] == 8) charging_level_data[0] = CHARGE_CURRENT_900_00_MA; else if (charging_level_data[0] == 9) charging_level_data[0] = CHARGE_CURRENT_1000_00_MA; else if (charging_level_data[0] == 10) charging_level_data[0] = CHARGE_CURRENT_1100_00_MA; else if (charging_level_data[0] == 11) charging_level_data[0] = CHARGE_CURRENT_1200_00_MA; else if (charging_level_data[0] == 12) charging_level_data[0] = CHARGE_CURRENT_1300_00_MA; else if (charging_level_data[0] == 13) charging_level_data[0] = CHARGE_CURRENT_1400_00_MA; else if (charging_level_data[0] == 14) charging_level_data[0] = CHARGE_CURRENT_1500_00_MA; else if (charging_level_data[0] == 15) charging_level_data[0] = CHARGE_CURRENT_1600_00_MA; else charging_level_data[0] = CHARGE_CURRENT_450_00_MA; wake_up_bat(); bm_notice("**** unlocked_ioctl : set_Charger_Current:%d\n", charging_level_data[0]); break; #endif /* add for meta tool------------------------------- */ case Get_META_BAT_VOL: adc_out_data[0] = battery_get_bat_voltage(); if (copy_to_user(user_data_addr, adc_out_data, sizeof(adc_out_data))) { mutex_unlock(&gm.fg_mutex); return -EFAULT; } bm_notice("**** unlocked_ioctl :Get_META_BAT_VOL Done!\n"); break; case Get_META_BAT_SOC: adc_out_data[0] = battery_get_uisoc(); if (copy_to_user(user_data_addr, adc_out_data, sizeof(adc_out_data))) { mutex_unlock(&gm.fg_mutex); return -EFAULT; } bm_notice("**** unlocked_ioctl :Get_META_BAT_SOC Done!\n"); break; case Get_META_BAT_CAR_TUNE_VALUE: adc_out_data[0] = fg_cust_data.car_tune_value; bm_err("Get_BAT_CAR_TUNE_VALUE, res=%d\n", adc_out_data[0]); if (copy_to_user(user_data_addr, adc_out_data, sizeof(adc_out_data))) { mutex_unlock(&gm.fg_mutex); return -EFAULT; } bm_notice("**** unlocked_ioctl :Get_META_BAT_CAR_TUNE_VALUE Done!\n"); break; case Set_META_BAT_CAR_TUNE_VALUE: /* meta tool input: adc_in_data[1] (mA)*/ /* Send cali_current to hal to calculate car_tune_value*/ temp_car_tune = battery_meter_meta_tool_cali_car_tune(adc_in_data[1]); /* return car_tune_value to meta tool in adc_out_data[0] */ if (temp_car_tune >= 900 && temp_car_tune <= 1100) fg_cust_data.car_tune_value = temp_car_tune; else bm_err("car_tune_value invalid:%d\n", temp_car_tune); adc_out_data[0] = temp_car_tune; if (copy_to_user(user_data_addr, adc_out_data, sizeof(adc_out_data))) { mutex_unlock(&gm.fg_mutex); return -EFAULT; } bm_err("**** unlocked_ioctl Set_BAT_CAR_TUNE_VALUE[%d], result=%d, ret=%d\n", adc_in_data[1], adc_out_data[0], ret); break; case Set_BAT_DISABLE_NAFG: isdisNAFG = adc_in_data[1]; if (isdisNAFG == 1) { gm.cmd_disable_nafg = true; wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_DISABLE_NAFG, 1); } else if (isdisNAFG == 0) { gm.cmd_disable_nafg = false; wakeup_fg_algo_cmd( FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_DISABLE_NAFG, 0); } bm_debug("unlocked_ioctl Set_BAT_DISABLE_NAFG,isdisNAFG=%d [%d]\n", isdisNAFG, adc_in_data[1]); break; /* add bing meta tool------------------------------- */ case Set_CARTUNE_TO_KERNEL: temp_car_tune = adc_in_data[1]; if (temp_car_tune > 500 && temp_car_tune < 1500) fg_cust_data.car_tune_value = temp_car_tune; bm_err("**** unlocked_ioctl Set_CARTUNE_TO_KERNEL[%d,%d], ret=%d\n", adc_in_data[0], adc_in_data[1], ret); break; default: bm_err("**** unlocked_ioctl unknown IOCTL: 0x%08x\n", cmd); g_ADC_Cali = false; mutex_unlock(&gm.fg_mutex); return -EINVAL; } mutex_unlock(&gm.fg_mutex); return 0; } static int adc_cali_open(struct inode *inode, struct file *file) { return 0; } static int adc_cali_release(struct inode *inode, struct file *file) { return 0; } static unsigned long bat_node; static int fb_early_init_dt_get_chosen( unsigned long node, const char *uname, int depth, void *data) { if (depth != 1 || (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) return 0; bat_node = node; return 1; } static const struct file_operations adc_cali_fops = { .owner = THIS_MODULE, .unlocked_ioctl = adc_cali_ioctl, #if IS_ENABLED(CONFIG_COMPAT) .compat_ioctl = compat_adc_cali_ioctl, #endif .open = adc_cali_open, .release = adc_cali_release, }; /*************************************/ static struct wakeup_source *battery_lock; static int __init battery_probe(struct platform_device *dev) { int ret_device_file = 0; int ret; struct class_device *class_dev = NULL; const char *fg_swocv_v = NULL; char fg_swocv_v_tmp[10]; int fg_swocv_v_len = 0; const char *fg_swocv_i = NULL; char fg_swocv_i_tmp[10]; int fg_swocv_i_len = 0; const char *shutdown_time = NULL; char shutdown_time_tmp[10]; int shutdown_time_len = 0; const char *boot_voltage = NULL; char boot_voltage_tmp[10]; int boot_voltage_len = 0; battery_lock = wakeup_source_register(NULL, "battery wakelock"); __pm_stay_awake(battery_lock); /********* adc_cdev **********/ ret = alloc_chrdev_region(&adc_cali_devno, 0, 1, ADC_CALI_DEVNAME); if (ret) bm_notice("Error: Can't Get Major number for adc_cali\n"); adc_cali_cdev = cdev_alloc(); adc_cali_cdev->owner = THIS_MODULE; adc_cali_cdev->ops = &adc_cali_fops; ret = cdev_add(adc_cali_cdev, adc_cali_devno, 1); if (ret) bm_notice("adc_cali Error: cdev_add\n"); adc_cali_major = MAJOR(adc_cali_devno); adc_cali_class = class_create(THIS_MODULE, ADC_CALI_DEVNAME); class_dev = (struct class_device *)device_create(adc_cali_class, NULL, adc_cali_devno, NULL, ADC_CALI_DEVNAME); /*****************************/ mtk_battery_init(dev); /* Power supply class */ #if !defined(CONFIG_MTK_DISABLE_GAUGE) battery_main.psy = power_supply_register( &(dev->dev), &battery_main.psd, NULL); if (IS_ERR(battery_main.psy)) { bm_err("[BAT_probe] power_supply_register Battery Fail !!\n"); ret = PTR_ERR(battery_main.psy); return ret; } bm_err("[BAT_probe] power_supply_register Battery Success !!\n"); #endif ret = device_create_file(&(dev->dev), &dev_attr_Battery_Temperature); ret = device_create_file(&(dev->dev), &dev_attr_UI_SOC); /* sysfs node */ ret_device_file = device_create_file(&(dev->dev), &dev_attr_uisoc_update_type); ret_device_file = device_create_file(&(dev->dev), &dev_attr_disable_nafg); ret_device_file = device_create_file(&(dev->dev), &dev_attr_ntc_disable_nafg); ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_meter_resistance); ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_daemon_log_level); ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_daemon_disable); ret_device_file = device_create_file(&(dev->dev), &dev_attr_BAT_EC); ret_device_file = device_create_file(&(dev->dev), &dev_attr_BAT_HEALTH); ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_Battery_CurrentConsumption); ret_device_file = device_create_file(&(dev->dev), &dev_attr_Power_On_Voltage); ret_device_file = device_create_file(&(dev->dev), &dev_attr_Power_Off_Voltage); ret_device_file = device_create_file(&(dev->dev), &dev_attr_shutdown_condition_enable); ret_device_file = device_create_file(&(dev->dev), &dev_attr_reset_battery_cycle); ret_device_file = device_create_file(&(dev->dev), &dev_attr_reset_aging_factor); if (of_scan_flat_dt(fb_early_init_dt_get_chosen, NULL) > 0) fg_swocv_v = of_get_flat_dt_prop( bat_node, "atag,fg_swocv_v", &fg_swocv_v_len); if (fg_swocv_v == NULL) { bm_err(" fg_swocv_v == NULL len = %d\n", fg_swocv_v_len); } else { snprintf(fg_swocv_v_tmp, (fg_swocv_v_len + 1), "%s", fg_swocv_v); ret = kstrtoint(fg_swocv_v_tmp, 10, &gm.ptim_lk_v); bm_err(" fg_swocv_v = %s len %d fg_swocv_v_tmp %s ptim_lk_v[%d]\n", fg_swocv_v, fg_swocv_v_len, fg_swocv_v_tmp, gm.ptim_lk_v); } if (of_scan_flat_dt(fb_early_init_dt_get_chosen, NULL) > 0) fg_swocv_i = of_get_flat_dt_prop( bat_node, "atag,fg_swocv_i", &fg_swocv_i_len); if (fg_swocv_i == NULL) { bm_err(" fg_swocv_i == NULL len = %d\n", fg_swocv_i_len); } else { snprintf( fg_swocv_i_tmp, (fg_swocv_i_len + 1), "%s", fg_swocv_i); ret = kstrtoint(fg_swocv_i_tmp, 10, &gm.ptim_lk_i); bm_err(" fg_swocv_i = %s len %d fg_swocv_i_tmp %s ptim_lk_i[%d]\n", fg_swocv_i, fg_swocv_i_len, fg_swocv_i_tmp, gm.ptim_lk_i); } if (of_scan_flat_dt(fb_early_init_dt_get_chosen, NULL) > 0) shutdown_time = of_get_flat_dt_prop( bat_node, "atag,shutdown_time", &shutdown_time_len); if (shutdown_time == NULL) { bm_err(" shutdown_time == NULL len = %d\n", shutdown_time_len); } else { snprintf(shutdown_time_tmp, (shutdown_time_len + 1), "%s", shutdown_time); ret = kstrtoint(shutdown_time_tmp, 10, &gm.pl_shutdown_time); bm_err(" shutdown_time = %s len %d shutdown_time_tmp %s pl_shutdown_time[%d]\n", shutdown_time, shutdown_time_len, shutdown_time_tmp, gm.pl_shutdown_time); } if (of_scan_flat_dt(fb_early_init_dt_get_chosen, NULL) > 0) boot_voltage = of_get_flat_dt_prop( bat_node, "atag,boot_voltage", &boot_voltage_len); if (boot_voltage == NULL) { bm_err(" boot_voltage == NULL len = %d\n", boot_voltage_len); } else { snprintf( boot_voltage_tmp, (boot_voltage_len + 1), "%s", boot_voltage); ret = kstrtoint(boot_voltage_tmp, 10, &gm.pl_bat_vol); bm_err( "boot_voltage=%s len %d boot_voltage_tmp %s pl_bat_vol[%d]\n", boot_voltage, boot_voltage_len, boot_voltage_tmp, gm.pl_bat_vol); } gm.pbat_consumer = charger_manager_get_by_name(&(dev->dev), "charger"); if (gm.pbat_consumer != NULL) { gm.bat_nb.notifier_call = battery_callback; register_charger_manager_notifier(gm.pbat_consumer, &gm.bat_nb); } battery_debug_init(); __pm_relax(battery_lock); if (IS_ENABLED(CONFIG_POWER_EXT) || gm.disable_mtkbattery || IS_ENABLED(CONFIG_MTK_DISABLE_GAUGE)) { bm_err("disable GM 3.0\n"); disable_fg(); } else if (is_recovery_mode()) battery_recovery_init(); mtk_battery_last_init(dev); gm.is_probe_done = true; return 0; } void battery_shutdown(struct platform_device *dev) { int fg_coulomb = 0; int shut_car_diff = 0; int verify_car = 0; fg_coulomb = gauge_get_coulomb(); if (gm.d_saved_car != 0) { shut_car_diff = fg_coulomb - gm.d_saved_car; gauge_dev_set_info(gm.gdev, GAUGE_SHUTDOWN_CAR, shut_car_diff); /* ready for writing to PMIC_RG */ } gauge_dev_get_info(gm.gdev, GAUGE_SHUTDOWN_CAR, &verify_car); bm_err("******** %s!! car=[o:%d,new:%d,diff:%d v:%d]********\n", __func__, gm.d_saved_car, fg_coulomb, shut_car_diff, verify_car); gm.fix_coverity = 1; } static int battery_suspend(struct platform_device *dev, pm_message_t state) { bm_err("******** %s!! iavg=%d ***GM3 disable:%d %d %d %d tmp_intr:%d***\n", __func__, gm.hw_status.iavg_intr_flag, gm.disableGM30, fg_cust_data.disable_nafg, gm.ntc_disable_nafg, gm.cmd_disable_nafg, gm.enable_tmp_intr_suspend); if (gm.enable_tmp_intr_suspend == 0) { gauge_enable_interrupt(FG_RG_INT_EN_BAT_TEMP_H, 0); gauge_enable_interrupt(FG_RG_INT_EN_BAT_TEMP_L, 0); enable_bat_temp_det(0); } if (gauge_get_hw_version() >= GAUGE_HW_V2000 && gm.hw_status.iavg_intr_flag == 1) { gauge_enable_interrupt(FG_IAVG_H_NO, 0); if (gm.hw_status.iavg_lt > 0) gauge_enable_interrupt(FG_IAVG_L_NO, 0); } return 0; } static int battery_resume(struct platform_device *dev) { zcv_filter_add(&gm.zcvf); zcv_filter_dump(&gm.zcvf); zcv_check(&gm.zcvf); bm_err("******** %s!! iavg=%d ***GM3 disable:%d %d %d %d***\n", __func__, gm.hw_status.iavg_intr_flag, gm.disableGM30, fg_cust_data.disable_nafg, gm.ntc_disable_nafg, gm.cmd_disable_nafg); if (is_fg_disabled()) { bm_info("[%s] fg_disabled, don't enable iavg interrupt\n", __func__); } else { if (gauge_get_hw_version() >= GAUGE_HW_V2000 && gm.hw_status.iavg_intr_flag == 1) { gauge_enable_interrupt(FG_IAVG_H_NO, 1); if (gm.hw_status.iavg_lt > 0) gauge_enable_interrupt(FG_IAVG_L_NO, 1); } } /* reset nafg monitor time to avoid suspend for too long case */ get_monotonic_boottime(&gm.last_nafg_update_time); fg_update_sw_iavg(); if (is_fg_disabled()) { bm_info("[%s] fg_disabled, don't enable bat_temp interrupt\n", __func__); } else { if (gm.enable_tmp_intr_suspend == 0) { gauge_enable_interrupt(FG_RG_INT_EN_BAT_TEMP_H, 1); gauge_enable_interrupt(FG_RG_INT_EN_BAT_TEMP_L, 1); enable_bat_temp_det(1); } } return 0; } #ifdef CONFIG_OF static const struct of_device_id mtk_bat_of_match[] = { {.compatible = "mediatek,bat_gm30",}, {}, }; MODULE_DEVICE_TABLE(of, mtk_bat_of_match); #endif struct platform_device battery_device = { #if defined(CONFIG_BATTERY_SAMSUNG) .name = "mtk-fg-battery", #else .name = "battery", #endif .id = -1, }; static struct platform_driver battery_driver_probe = { .probe = battery_probe, .remove = NULL, .shutdown = battery_shutdown, .suspend = battery_suspend, .resume = battery_resume, .driver = { #if defined(CONFIG_BATTERY_SAMSUNG) .name = "mtk-fg-battery", #else .name = "battery", #endif #ifdef CONFIG_OF .of_match_table = mtk_bat_of_match, #endif }, }; static int __init battery_init(void) { struct netlink_kernel_cfg cfg = { .input = nl_data_handler, }; int ret; gm.daemo_nl_sk = netlink_kernel_create(&init_net, NETLINK_FGD, &cfg); bm_err("netlink_kernel_create protol= %d\n", NETLINK_FGD); if (gm.daemo_nl_sk == NULL) { bm_err("netlink_kernel_create error\n"); return -1; } bm_err("netlink_kernel_create ok\n"); #ifdef CONFIG_OF /* register battery_device by DTS */ #else ret = platform_device_register(&battery_device); #endif ret = platform_driver_register(&battery_driver_probe); bm_err("[%s] Initialization : DONE\n", __func__); return 0; } static void __exit battery_exit(void) { } module_init(battery_init); module_exit(battery_exit); MODULE_AUTHOR("Weiching Lin"); MODULE_DESCRIPTION("Battery Device Driver"); MODULE_LICENSE("GPL");