| /* |
| * sm5713_fuelgauge.c |
| * Samsung sm5713 Fuel Gauge Driver |
| * |
| * Copyright (C) 2015 Samsung Electronics |
| * |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| /* #define BATTERY_LOG_MESSAGE */ |
| #if defined(CONFIG_BATTERY_SAMSUNG_V2) |
| #include "include/sec_battery.h" |
| #else |
| #include <linux/battery/sec_battery.h> |
| #endif |
| #include <linux/mfd/sm5713-private.h> |
| #include "include/fuelgauge/sm5713_fuelgauge.h" |
| #include <linux/of_gpio.h> |
| #include <linux/debugfs.h> |
| #include <linux/seq_file.h> |
| |
| |
| static enum power_supply_property sm5713_fuelgauge_props[] = { |
| }; |
| |
| static char *sm5713_fg_supplied_to[] = { |
| "sm5713-fuelgauge", |
| }; |
| |
| #define MINVAL(a, b) ((a <= b) ? a : b) |
| #define MAXVAL(a, b) ((a > b) ? a : b) |
| |
| #define LIMIT_N_CURR_MIXFACTOR -2000 |
| #define TABLE_READ_COUNT 2 |
| #define FG_ABNORMAL_RESET -1 |
| #define IGNORE_N_I_OFFSET 1 |
| |
| #define SM5713_FG_FULL_DEBUG 1 |
| #define I2C_ERROR_COUNT_MAX 5 |
| |
| #ifdef ENABLE_SM5713_MQ_FUNCTION |
| static int sm5713_get_full_chg_mq (struct sm5713_fuelgauge_data *fuelgauge); |
| static void sm5713_set_full_chg_mq (struct sm5713_fuelgauge_data *fuelgauge, int mq); |
| static void sm5713_meas_mq_suspend (struct sm5713_fuelgauge_data *fuelgauge); |
| static void sm5713_meas_mq_resume (struct sm5713_fuelgauge_data *fuelgauge); |
| static void sm5713_meas_mq_start (struct sm5713_fuelgauge_data *fuelgauge); |
| static int sm5713_meas_eq_dump (struct sm5713_fuelgauge_data *fuelgauge); |
| static int sm5713_meas_mq_dump (struct sm5713_fuelgauge_data *fuelgauge); |
| static void sm5713_meas_mq_off (struct sm5713_fuelgauge_data *fuelgauge); |
| #endif |
| |
| void sm5713_adabt_full_offset(struct sm5713_fuelgauge_data *fuelgauge); |
| static bool sm5713_fg_init(struct sm5713_fuelgauge_data *fuelgauge, bool is_surge); |
| |
| |
| |
| static int sm5713_device_id = -1; |
| |
| /* static unsigned int lpcharge = 0; */ |
| |
| enum sm5713_battery_table_type { |
| DISCHARGE_TABLE = 0, |
| SOC_TABLE, |
| TABLE_MAX, |
| }; |
| |
| bool sm5713_fg_fuelalert_init(struct sm5713_fuelgauge_data *fuelgauge, |
| int soc); |
| |
| #if !defined(CONFIG_SEC_FACTORY) |
| static void sm5713_fg_periodic_read(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| static struct timespec old_ts = {0, }; |
| struct timespec c_ts = {0, }; |
| u8 reg; |
| int i; |
| int data[0x10]; |
| char *str = NULL; |
| |
| c_ts = ktime_to_timespec(ktime_get_boottime()); |
| if ((unsigned long)(c_ts.tv_sec - old_ts.tv_sec) <= 180 && old_ts.tv_sec != 0) { /*3 min*/ |
| pr_info("%s: skip old(%ld) current(%ld)\n", __func__, old_ts.tv_sec, c_ts.tv_sec); |
| return; |
| } |
| old_ts = c_ts; |
| |
| str = kzalloc(sizeof(char)*1024, GFP_KERNEL); |
| if (!str) |
| return; |
| |
| for (i = 0; i <= 0xB; i++) { |
| if (i == 7) |
| i = 8; |
| for (reg = 0; reg < 0x10; reg++) { |
| if ((i == 0) && ((reg == 0) || (reg == 2))) { |
| data[reg] = 0; |
| reg++; |
| } |
| data[reg] = sm5713_read_word(fuelgauge->i2c, reg + (i * 0x10)); |
| if (data[reg] < 0) { |
| kfree(str); |
| return; |
| } |
| } |
| sprintf(str+strlen(str), |
| "%02x:%04x,%04x,%04x,%04x,%04x,%04x,%04x,%04x,", |
| i, data[0x00], data[0x01], data[0x02], data[0x03], |
| data[0x04], data[0x05], data[0x06], data[0x07]); |
| sprintf(str+strlen(str), |
| "%04x,%04x,%04x,%04x,%04x,%04x,%04x,%04x,", |
| data[0x08], data[0x09], data[0x0a], data[0x0b], |
| data[0x0c], data[0x0d], data[0x0e], data[0x0f]); |
| if (!fuelgauge->initial_update_of_soc) { |
| usleep_range(1000, 2000); |
| } |
| } |
| |
| pr_info("[FG_ALL] %s", str); |
| pr_info("\n"); |
| |
| kfree(str); |
| } |
| #endif |
| |
| static bool sm5713_fg_check_reg_init_need(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret; |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_OP_STATUS); |
| |
| if ((ret & INIT_CHECK_MASK) == DISABLE_RE_INIT) { |
| pr_info("%s: SM5713_REG_FG_OP_STATUS : 0x%x , return FALSE NO init need\n", __func__, ret); |
| return 0; |
| } else { |
| pr_info("%s: SM5713_REG_FG_OP_STATUS : 0x%x , return TRUE init need!!!!\n", __func__, ret); |
| return 1; |
| } |
| } |
| |
| void sm5713_cal_avg_vbat(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| if (fuelgauge->info.batt_avgvoltage == 0) |
| fuelgauge->info.batt_avgvoltage = fuelgauge->info.batt_voltage; |
| |
| else if (fuelgauge->info.batt_voltage == 0 && fuelgauge->info.p_batt_voltage == 0) |
| fuelgauge->info.batt_avgvoltage = 3400; |
| |
| else if (fuelgauge->info.batt_voltage == 0) |
| fuelgauge->info.batt_avgvoltage = |
| ((fuelgauge->info.batt_avgvoltage) + (fuelgauge->info.p_batt_voltage))/2; |
| |
| else if (fuelgauge->info.p_batt_voltage == 0) |
| fuelgauge->info.batt_avgvoltage = |
| ((fuelgauge->info.batt_avgvoltage) + (fuelgauge->info.batt_voltage))/2; |
| |
| else |
| fuelgauge->info.batt_avgvoltage = |
| ((fuelgauge->info.batt_avgvoltage*2) + |
| (fuelgauge->info.p_batt_voltage+fuelgauge->info.batt_voltage))/4; |
| |
| #ifdef SM5713_FG_FULL_DEBUG |
| pr_info("%s: batt_avgvoltage = %d\n", __func__, fuelgauge->info.batt_avgvoltage); |
| #endif |
| |
| return; |
| } |
| |
| void sm5713_voffset_cancel(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int volt_slope, mohm_volt_cal; |
| int fg_temp_gap = 0, volt_cal = 0, fg_delta_volcal = 0, pn_volt_slope = 0, volt_offset = 0; |
| |
| if ((sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_AUX_STAT) & 0x02) || |
| factory_mode) { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_VOLT_CAL, fuelgauge->info.volt_cal[1]); |
| } else { |
| /*set vbat offset cancel start */ |
| volt_slope = fuelgauge->info.volt_cal[0] & 0xFF00; |
| mohm_volt_cal = fuelgauge->info.volt_cal[0] & 0x00FF; |
| if (fuelgauge->info.enable_v_offset_cancel_p) { |
| if (fuelgauge->is_charging && (fuelgauge->info.batt_current > fuelgauge->info.v_offset_cancel_level)) { |
| if (mohm_volt_cal & 0x0080) { |
| mohm_volt_cal = -(mohm_volt_cal & 0x007F); |
| } |
| mohm_volt_cal = mohm_volt_cal - (fuelgauge->info.batt_current/(fuelgauge->info.v_offset_cancel_mohm * 13)); /* ((curr*0.001)*0.006)*2048 -> 6mohm */ |
| if (mohm_volt_cal < 0) { |
| mohm_volt_cal = -mohm_volt_cal; |
| mohm_volt_cal = mohm_volt_cal|0x0080; |
| } |
| } |
| } |
| if (fuelgauge->info.enable_v_offset_cancel_n) { |
| if (!(fuelgauge->is_charging) && (fuelgauge->info.batt_current < -(fuelgauge->info.v_offset_cancel_level))) { |
| if (fuelgauge->info.volt_cal[0] & 0x0080) { |
| mohm_volt_cal = -(mohm_volt_cal & 0x007F); |
| } |
| mohm_volt_cal = mohm_volt_cal - (fuelgauge->info.batt_current/(fuelgauge->info.v_offset_cancel_mohm * 13)); /* ((curr*0.001)*0.006)*2048 -> 6mohm */ |
| if (mohm_volt_cal < 0) { |
| mohm_volt_cal = -mohm_volt_cal; |
| mohm_volt_cal = mohm_volt_cal|0x0080; |
| } |
| } |
| } |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_VOLT_CAL, ((mohm_volt_cal & 0x00FF) | (volt_slope & 0xFF00))); |
| pr_info("%s: <%d %d %d %d> volt_cal = 0x%x, volt_slope = 0x%x, mohm_volt_cal = 0x%x\n", |
| __func__, fuelgauge->info.enable_v_offset_cancel_p, fuelgauge->info.enable_v_offset_cancel_n |
| , fuelgauge->info.v_offset_cancel_level, fuelgauge->info.v_offset_cancel_mohm |
| , fuelgauge->info.volt_cal[0], volt_slope, mohm_volt_cal); |
| /* set vbat offset cancel end */ |
| |
| fg_temp_gap = (fuelgauge->info.temp_fg/10) - fuelgauge->info.temp_std; |
| |
| volt_cal = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_VOLT_CAL); |
| volt_offset = volt_cal & 0x00FF; |
| pn_volt_slope = fuelgauge->info.volt_cal[0] & 0xFF00; |
| |
| if (fuelgauge->info.en_fg_temp_volcal) { |
| fg_delta_volcal = (fg_temp_gap / fuelgauge->info.fg_temp_volcal_denom)*fuelgauge->info.fg_temp_volcal_fact; |
| pn_volt_slope = pn_volt_slope + (fg_delta_volcal<<8); |
| volt_cal = pn_volt_slope | volt_offset; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_VOLT_CAL, volt_cal); |
| } |
| } |
| |
| return; |
| } |
| |
| static unsigned int sm5713_get_vbat(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret = 0; |
| unsigned int vbat = 0; /* = 3500; 3500 means 3500mV*/ |
| |
| sm5713_voffset_cancel(fuelgauge); |
| |
| if (fuelgauge->info.is_read_vpack) |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_VOLTAGE_CHGOUT); |
| else |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_VOLTAGE_VBAT); |
| |
| if ((sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_AUX_STAT) & 0x0002) || |
| factory_mode) { |
| if (fuelgauge->isjigmoderealvbat) |
| pr_info("%s : nENQ4 high JIG_ON, BUT need real VBAT \n", __func__, ret); |
| else { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_BAT_PTT1, 0x0109); |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_VOLTAGE_VSYS); |
| pr_info("%s : nENQ4 high JIG_ON, vsys register read result 0x%x\n", __func__, ret); |
| } |
| } |
| else { |
| if(sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_BAT_PTT1) != 0x0100) { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_BAT_PTT1, 0x0100); |
| } |
| } |
| |
| if (ret < 0) { |
| pr_err("%s: read vbat reg fail", __func__); |
| vbat = 4000; |
| } else { |
| vbat = ((ret&0x3fff)*1000)>>11; |
| } |
| fuelgauge->info.batt_voltage = vbat; |
| |
| sm5713_cal_avg_vbat(fuelgauge); |
| |
| if ((fuelgauge->vempty_mode == VEMPTY_MODE_SW_VALERT) && |
| (vbat >= fuelgauge->battery_data->sw_v_empty_recover_vol)) { |
| fuelgauge->vempty_mode = VEMPTY_MODE_SW_RECOVERY; |
| |
| sm5713_fg_fuelalert_init(fuelgauge, |
| fuelgauge->pdata->fuel_alert_soc); |
| pr_info("%s : Recoverd from SW V EMPTY Activation\n", __func__); |
| |
| } |
| |
| return vbat; |
| } |
| |
| static unsigned int sm5713_get_ocv(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret; |
| unsigned int ocv; /* = 3500; *//*3500 means 3500mV*/ |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_OCV); |
| if (ret < 0) { |
| pr_err("%s: read ocv reg fail\n", __func__); |
| ocv = 4000; |
| } else { |
| ocv = ((ret&0x7fff)*1000)>>11; |
| } |
| |
| fuelgauge->info.batt_ocv = ocv; |
| |
| return ocv; |
| } |
| |
| void sm5713_cal_avg_current(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| if (fuelgauge->info.batt_avgcurrent == 0) |
| fuelgauge->info.batt_avgcurrent = fuelgauge->info.batt_current; |
| |
| else if (fuelgauge->info.batt_avgcurrent == 0 && fuelgauge->info.p_batt_current == 0) |
| fuelgauge->info.batt_avgcurrent = fuelgauge->info.batt_current; |
| |
| else if (fuelgauge->info.batt_current == 0) |
| fuelgauge->info.batt_avgcurrent = |
| ((fuelgauge->info.batt_avgcurrent) + (fuelgauge->info.p_batt_current))/2; |
| |
| else if (fuelgauge->info.p_batt_current == 0) |
| fuelgauge->info.batt_avgcurrent = |
| ((fuelgauge->info.batt_avgcurrent) + (fuelgauge->info.batt_current))/2; |
| |
| else |
| fuelgauge->info.batt_avgcurrent = |
| ((fuelgauge->info.batt_avgcurrent*2) + |
| (fuelgauge->info.p_batt_current+fuelgauge->info.batt_current))/4; |
| |
| return; |
| } |
| |
| static int sm5713_get_curr(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret; |
| int curr = 0; /* = 1000; 1000 means 1000mA*/ |
| |
| #ifdef ENABLE_FULL_OFFSET |
| sm5713_adabt_full_offset(fuelgauge); |
| #endif |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_CURRENT); |
| if (ret < 0) { |
| pr_err("%s: read curr reg fail", __func__); |
| curr = 0; |
| } else { |
| curr = ((ret&0x3fff)*1000)>>11; |
| if (ret&0x8000) { |
| curr *= -1; |
| } |
| } |
| |
| fuelgauge->info.batt_current = curr; |
| |
| sm5713_cal_avg_current(fuelgauge); |
| |
| return curr; |
| } |
| |
| static int sm5713_get_temperature(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret; |
| int temp; /* = 250; 250 means 25.0oC*/ |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_TEMPERATURE); |
| if (ret < 0) { |
| pr_err("%s: read temp reg fail", __func__); |
| temp = 0; |
| } else { |
| temp = ((ret&0x7FF0)*10)>>8; |
| if (ret&0x8000) { |
| temp *= -1; |
| } |
| } |
| fuelgauge->info.temp_fg = temp; |
| |
| return temp; |
| } |
| |
| static int sm5713_get_cycle(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret; |
| int cycle; |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_SOC_CYCLE); |
| if (ret < 0) { |
| pr_err("%s: read cycle reg fail", __func__); |
| cycle = 0; |
| } else { |
| cycle = ret&0x03FF; |
| } |
| fuelgauge->info.batt_soc_cycle = cycle; |
| |
| return cycle; |
| } |
| |
| static int sm5713_get_asoc(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ctrl, info, soh, pre_soh, h_flag, c_flag, delta_t, temp; |
| |
| ctrl = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_AGING_CTRL); |
| info = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_AGING_INFO); |
| pre_soh = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_2); |
| c_flag = (sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_SOC_CYCLE) >> 4 ) % 2; |
| |
| h_flag = (pre_soh & 0x80)>>7; |
| pre_soh = pre_soh & 0x7F; |
| pr_info("%s : asoc = %d, ctrl = 0x%x, info = 0x%x, pre = 0x%x, t = %d \n", __func__, fuelgauge->info.soh, ctrl, info, pre_soh, fuelgauge->info.temperature); |
| ctrl = ctrl & 0x0200; |
| if (ctrl != 0x0200) { |
| soh = pre_soh; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_AGING_CTRL, fuelgauge->info.age_cntl); |
| } else { |
| info = info & 0x007F; |
| soh = ((info * 100) / 128) + 2; |
| } |
| |
| if ((soh > 100) || (soh <= 10)) |
| soh = 100; |
| |
| if ((pre_soh > 100) || (pre_soh <= 10)) |
| pre_soh = 100; |
| |
| delta_t = fuelgauge->info.temperature/10 - fuelgauge->info.temp_std; |
| if (delta_t >= 0) { |
| if (soh < pre_soh) { |
| if (c_flag != h_flag) { |
| pre_soh = pre_soh-1; |
| temp = (c_flag<<7) | pre_soh; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_2, temp); |
| pr_info("%s : pre_soh update to %d, write 0x%x\n", __func__, pre_soh, temp); |
| } |
| } |
| } |
| soh = pre_soh; |
| |
| fuelgauge->info.soh = soh; |
| |
| return fuelgauge->info.soh; |
| } |
| |
| |
| static void sm5713_vbatocv_check(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| if ((abs(fuelgauge->info.batt_current) < 50) || |
| ((fuelgauge->is_charging) && (fuelgauge->info.batt_current < (fuelgauge->info.top_off)) && |
| (fuelgauge->info.batt_current > (fuelgauge->info.top_off/3)) && (fuelgauge->info.batt_soc >= 900))) { |
| if (abs(fuelgauge->info.batt_ocv-fuelgauge->info.batt_voltage) > 30) { /* 30mV over */ |
| fuelgauge->info.iocv_error_count++; |
| } |
| |
| pr_info("%s: sm5713 FG iocv_error_count (%d)\n", __func__, fuelgauge->info.iocv_error_count); |
| |
| if (fuelgauge->info.iocv_error_count > 5) /* prevent to overflow */ |
| fuelgauge->info.iocv_error_count = 6; |
| } else { |
| fuelgauge->info.iocv_error_count = 0; |
| } |
| |
| if (fuelgauge->info.iocv_error_count > 5) { |
| pr_info("%s: p_v - v = (%d)\n", __func__, fuelgauge->info.p_batt_voltage - fuelgauge->info.batt_voltage); |
| if (abs(fuelgauge->info.p_batt_voltage - fuelgauge->info.batt_voltage) > 15) { /* 15mV over */ |
| fuelgauge->info.iocv_error_count = 0; |
| } else { |
| /* mode change to mix RS manual mode */ |
| pr_info("%s: mode change to mix RS manual mode\n", __func__); |
| /* RS manual value write */ |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MAX, fuelgauge->info.rs_value[0]+5); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MIN, fuelgauge->info.rs_value[0]-5); |
| } |
| } else { |
| /* voltage mode with 3.4V */ |
| if (fuelgauge->vempty_mode != VEMPTY_MODE_HW) { |
| if ((fuelgauge->info.p_batt_voltage < fuelgauge->info.n_tem_poff) && |
| (fuelgauge->info.batt_voltage < fuelgauge->info.n_tem_poff) && (!fuelgauge->is_charging)) { |
| pr_info("%s: mode change to normal tem mix RS manual mode\n", __func__); |
| /* mode change to mix RS manual mode */ |
| /* RS manual value write */ |
| if ((fuelgauge->info.p_batt_voltage < |
| (fuelgauge->info.n_tem_poff - fuelgauge->info.n_tem_poff_offset)) && |
| (fuelgauge->info.batt_voltage < |
| (fuelgauge->info.n_tem_poff - fuelgauge->info.n_tem_poff_offset))) { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MAX, (fuelgauge->info.rs_value[0]>>1)+5); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MIN, (fuelgauge->info.rs_value[0]>>1)-5); |
| } else { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MAX, fuelgauge->info.rs_value[0]+5); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MIN, fuelgauge->info.rs_value[0]-5); |
| } |
| } else { |
| pr_info("%s: mode change to mix RS auto mode\n", __func__); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MAX, fuelgauge->info.rs_value[3]); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MIN, fuelgauge->info.rs_value[4]); |
| } |
| } else { /* voltage mode with 3.25V, VEMPTY_MODE_HW mode */ |
| if ((fuelgauge->info.p_batt_voltage < fuelgauge->info.l_tem_poff) && |
| (fuelgauge->info.batt_voltage < fuelgauge->info.l_tem_poff) && (!fuelgauge->is_charging)) { |
| pr_info("%s: mode change to normal tem mix RS manual mode\n", __func__); |
| /* mode change to mix RS manual mode */ |
| /* RS manual value write */ |
| if ((fuelgauge->info.p_batt_voltage < |
| (fuelgauge->info.l_tem_poff - fuelgauge->info.l_tem_poff_offset)) && |
| (fuelgauge->info.batt_voltage < |
| (fuelgauge->info.l_tem_poff - fuelgauge->info.l_tem_poff_offset))) { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MAX, (fuelgauge->info.rs_value[0]>>1)+5); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MIN, (fuelgauge->info.rs_value[0]>>1)-5); |
| } else { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MAX, fuelgauge->info.rs_value[0]+5); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MIN, fuelgauge->info.rs_value[0]-5); |
| } |
| } else { |
| pr_info("%s: mode change to mix RS auto mode\n", __func__); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MAX, fuelgauge->info.rs_value[3]); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MIN, fuelgauge->info.rs_value[4]); |
| } |
| } |
| } |
| fuelgauge->info.p_batt_voltage = fuelgauge->info.batt_voltage; |
| fuelgauge->info.p_batt_current = fuelgauge->info.batt_current; |
| /* iocv error case cover end */ |
| } |
| |
| #ifdef ENABLE_FULL_OFFSET |
| void sm5713_adabt_full_offset(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int fg_temp_gap; |
| int full_offset, i_offset, sign_offset, curr; |
| int curr_off, sign_origin, i_origin; |
| int sign_curr, i_curr; |
| int aux_stat; |
| |
| curr_off = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_DP_CSP_I_OFF); |
| aux_stat = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_AUX_STAT); |
| pr_info("%s: curr_off=%x, aux_stat=%x, flag_charge_health=%d, flag_chg_status=%d, flag_full_charge=%d\n", __func__, |
| curr_off, aux_stat, fuelgauge->info.flag_charge_health, fuelgauge->info.flag_chg_status, fuelgauge->info.flag_full_charge); |
| |
| if (fuelgauge->info.full_offset_enable > 0) { |
| if (((aux_stat & fuelgauge->info.aux_stat_base) == fuelgauge->info.aux_stat_check) |
| && (fuelgauge->info.batt_avgcurrent < fuelgauge->info.full_offset_margin) |
| && (fuelgauge->info.flag_chg_status == 0)) { |
| fg_temp_gap = (fuelgauge->info.temp_fg/10) - fuelgauge->info.temp_std; |
| if (abs(fg_temp_gap) < 10) { |
| curr = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_CURRENT); |
| sign_curr = curr & 0x8000; |
| i_curr = (curr & 0x7FFF)>>1; |
| if (sign_curr) { |
| i_curr = -i_curr; |
| } |
| |
| curr_off = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_DP_CSP_I_OFF); |
| sign_origin = curr_off & 0x0080; |
| i_origin = curr_off & 0x007F; |
| if (sign_origin) { |
| i_origin = -i_origin; |
| } |
| |
| full_offset = i_origin - i_curr + fuelgauge->info.full_extra_offset; |
| if (full_offset < 0) { |
| i_offset = -full_offset; |
| sign_offset = 1; |
| } else { |
| i_offset = full_offset; |
| sign_offset = 0; |
| } |
| |
| pr_info("%s: curr=%x, curr_off=%x, i_offset=%x, sign_offset=%d, full_offset_margin=%x, full_extra_offset=%x\n", |
| __func__, curr, curr_off, i_offset, sign_offset, fuelgauge->info.full_offset_margin, fuelgauge->info.full_extra_offset); |
| |
| if (sign_offset == 1) { |
| full_offset = i_offset|0x0080; |
| } |
| |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_EV_I_OFF, full_offset); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_CSP_I_OFF, full_offset); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_CSN_I_OFF, full_offset); |
| pr_info("%s: LAST i_offset=%x, sign_offset=%x, full_offset=%x\n", __func__, i_offset, sign_offset, full_offset); |
| } |
| } else { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_EV_I_OFF, fuelgauge->info.dp_ecv_i_off); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_CSP_I_OFF, fuelgauge->info.dp_csp_i_off); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_CSN_I_OFF, fuelgauge->info.dp_csn_i_off); |
| } |
| } |
| |
| return; |
| } |
| #endif |
| |
| #ifdef ENABLE_SM5713_MQ_FUNCTION |
| static int sm5713_get_full_chg_mq (struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int mq_raw, mq; |
| |
| mq_raw = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_1); |
| |
| mq = ((mq_raw&0x7FFF) * 1000) >> 11; |
| |
| pr_info("%s: raw = 0x%x, mq = %d\n", __func__, mq_raw, mq); |
| |
| return mq; |
| } |
| |
| static void sm5713_set_full_chg_mq (struct sm5713_fuelgauge_data *fuelgauge, int mq) |
| { |
| int mq_abs; |
| |
| mq_abs = mq; |
| mq_abs = (mq_abs << 11)/1000; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_1, mq_abs); |
| |
| pr_info("%s: mq = %d, abs = 0x%x\n", __func__, mq, mq_abs); |
| } |
| |
| static void sm5713_meas_mq_suspend (struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret, suspend_mq; |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_AUX_2); |
| |
| if ((ret & START_MQ) == START_MQ) { |
| suspend_mq = sm5713_meas_mq_dump(fuelgauge); |
| |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_START_MQ, ((suspend_mq<<11)/1000)); |
| pr_info("%s: suspend mode mq is <0x%x>, AUX_2 : <0x%x> \n", __func__, suspend_mq, ret); |
| |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_AUX_2, 0); |
| } else { |
| pr_info("%s: AUX_2 : <0x%x> \n", __func__, ret); |
| } |
| } |
| |
| static void sm5713_meas_mq_resume (struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret; |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_AUX_2); |
| pr_info("%s: SM5713_FG_REG_AUX_2 : <0x%x> \n", __func__, ret); |
| |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_AUX_2, START_MQ); |
| } |
| |
| static void sm5713_meas_mq_start (struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret, mq; |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_AUX_2); |
| pr_info("%s: SM5713_FG_REG_AUX_2 : <0x%x> \n", __func__, ret); |
| |
| if ((ret & START_MQ) != START_MQ) { |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_Q_MEAS_INIT); |
| if (ret == 0) { |
| mq = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_Q_EST); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_Q_MEAS_INIT, mq); |
| pr_info("%s: starting mq is <0x%x> \n", __func__, mq); |
| |
| msleep(50); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_AUX_2, START_MQ); |
| |
| /* full mq set */ |
| sm5713_set_full_chg_mq(fuelgauge, ((fuelgauge->info.cap * 1000) >> 11)); |
| } else { |
| pr_info("%s: read start mq is <0x%x> and resumed \n", __func__, ret); |
| sm5713_meas_mq_resume(fuelgauge); |
| } |
| } else { |
| pr_info("%s: sm5713_meas_mq_start already started : mq is <%d> \n", __func__, sm5713_meas_mq_dump(fuelgauge)); |
| } |
| } |
| |
| static int sm5713_meas_eq_dump (struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret, eq; |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_Q_EST); |
| pr_info("%s: SM5713_FG_REG_Q_EST : <0x%x> \n", __func__, ret); |
| |
| eq = ((ret&0x7FFF) * 1000) >> 11; |
| if (ret&0x8000) { |
| eq *= -1; |
| } |
| |
| if (eq == 0) { |
| eq = (fuelgauge->info.cap * 1000) >> 11; |
| pr_info("%s: eq is 0, It's abnormal, return cap : %d\n", __func__, eq); |
| } |
| |
| return eq; |
| } |
| |
| static int sm5713_meas_mq_dump (struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret, mq = 0, count = 0; |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_AUX_2); |
| |
| if ((ret & START_MQ) == START_MQ) { |
| pr_info("%s: AUX_2 : <0x%x> \n", __func__, ret); |
| |
| ret = START_MQ | DUMP_MQ; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_AUX_2, ret); |
| msleep(50); |
| ret = START_MQ; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_AUX_2, ret); |
| |
| for (count = 0; count < 5; count++) { |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_Q_DUMP); |
| if (ret == 0) { |
| pr_info("%s: SM5713_FG_REG_MQ : <0x%x> retry %d\n", __func__, ret, count); |
| msleep(50); |
| } else { |
| pr_info("%s: SM5713_FG_REG_MQ : <0x%x> OK count = %d\n", __func__, ret, count); |
| break; |
| } |
| } |
| |
| if (ret < 0) { |
| mq = ((fuelgauge->info.cap * 1000) >> 11); |
| pr_err("%s: sm5713_meas_mq_dump read fail!!!! return battery default value : %d\n", __func__, mq); |
| } else { |
| mq = ((ret&0x07FF) * 1000) >> 7; |
| if (ret&0x8000) { |
| mq *= -1; |
| } |
| } |
| |
| if (mq == 0) { |
| mq = sm5713_meas_eq_dump(fuelgauge); |
| pr_info("%s: mq is 0, It's abnormal, return eq : %d\n", __func__, mq); |
| } |
| } else { |
| mq = ((fuelgauge->info.cap * 1000) >> 11); |
| pr_info("%s: sm5713_meas_mq not started : return battery default value : %d, AUX_2 : <0x%x>\n", __func__, mq, ret); |
| } |
| |
| return mq; |
| } |
| |
| static void sm5713_meas_mq_off (struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int last_mq; |
| last_mq = sm5713_meas_mq_dump(fuelgauge); |
| |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_Q_MEAS_INIT, 0); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_AUX_2, 0); |
| |
| pr_info("%s: mq off completed, last_mq : %d\n", __func__, last_mq); |
| } |
| |
| #endif |
| |
| static void sm5713_dp_setup (struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_EV_I_OFF, fuelgauge->info.dp_ecv_i_off); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_CSP_I_OFF, fuelgauge->info.dp_csp_i_off); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_CSN_I_OFF, fuelgauge->info.dp_csn_i_off); |
| |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_EV_I_SLO, fuelgauge->info.dp_ecv_i_slo); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_CSP_I_SLO, fuelgauge->info.dp_csp_i_slo); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_CSN_I_SLO, fuelgauge->info.dp_csn_i_slo); |
| |
| pr_info("%s: dp_off : <0x%x 0x%x 0x%x> dp_slo : <0x%x 0x%x 0x%x>\n", |
| __func__, |
| fuelgauge->info.dp_ecv_i_off, fuelgauge->info.dp_csp_i_off, fuelgauge->info.dp_csn_i_off, |
| fuelgauge->info.dp_ecv_i_slo, fuelgauge->info.dp_csp_i_slo, fuelgauge->info.dp_csn_i_slo); |
| } |
| |
| static void sm5713_alg_setup (struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_EV_I_OFF, fuelgauge->info.ecv_i_off); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_CSP_I_OFF, fuelgauge->info.csp_i_off); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_CSN_I_OFF, fuelgauge->info.csn_i_off); |
| |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_EV_I_SLO, fuelgauge->info.ecv_i_slo); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_CSP_I_SLO, fuelgauge->info.csp_i_slo); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_CSN_I_SLO, fuelgauge->info.csn_i_slo); |
| |
| pr_info("%s: alg_off : <0x%x 0x%x 0x%x> alg_slo : <0x%x 0x%x 0x%x>\n", |
| __func__, |
| fuelgauge->info.ecv_i_off, fuelgauge->info.csp_i_off, fuelgauge->info.csn_i_off, |
| fuelgauge->info.ecv_i_slo, fuelgauge->info.csp_i_slo, fuelgauge->info.csn_i_slo); |
| } |
| |
| static void sm5713_coeff_setup (struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_VSBC_VOLT_TEMP_CAL, fuelgauge->info.coeff); |
| } |
| |
| static void sm5713_cal_carc (struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int curr_cal = 0, p_curr_cal = 0, n_curr_cal = 0, p_delta_cal = 0, n_delta_cal = 0, p_fg_delta_cal = 0, n_fg_delta_cal = 0, temp_curr_offset = 0; |
| int temp_gap, fg_temp_gap, mix_factor = 0; |
| |
| sm5713_vbatocv_check(fuelgauge); |
| |
| if (fuelgauge->is_charging || (fuelgauge->info.batt_current < LIMIT_N_CURR_MIXFACTOR)) { |
| mix_factor = fuelgauge->info.rs_value[1]; |
| } else { |
| mix_factor = fuelgauge->info.rs_value[2]; |
| } |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MIX_FACTOR, mix_factor); |
| |
| fg_temp_gap = (fuelgauge->info.temp_fg/10) - fuelgauge->info.temp_std; |
| |
| |
| temp_curr_offset = fuelgauge->info.ecv_i_off; |
| if (fuelgauge->info.en_high_fg_temp_offset && (fg_temp_gap > 0)) { |
| if (temp_curr_offset & 0x0080) { |
| temp_curr_offset = -(temp_curr_offset & 0x007F); |
| } |
| temp_curr_offset = temp_curr_offset + (fg_temp_gap / fuelgauge->info.high_fg_temp_offset_denom)*fuelgauge->info.high_fg_temp_offset_fact; |
| if (temp_curr_offset < 0) { |
| temp_curr_offset = -temp_curr_offset; |
| temp_curr_offset = temp_curr_offset|0x0080; |
| } |
| } else if (fuelgauge->info.en_low_fg_temp_offset && (fg_temp_gap < 0)) { |
| if (temp_curr_offset & 0x0080) { |
| temp_curr_offset = -(temp_curr_offset & 0x007F); |
| } |
| temp_curr_offset = temp_curr_offset + ((-fg_temp_gap) / fuelgauge->info.low_fg_temp_offset_denom)*fuelgauge->info.low_fg_temp_offset_fact; |
| if (temp_curr_offset < 0) { |
| temp_curr_offset = -temp_curr_offset; |
| temp_curr_offset = temp_curr_offset|0x0080; |
| } |
| } |
| temp_curr_offset = temp_curr_offset | (temp_curr_offset<<8); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_EV_I_OFF, temp_curr_offset); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_CSP_I_OFF, temp_curr_offset); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_CSN_I_OFF, temp_curr_offset); |
| |
| n_curr_cal = (fuelgauge->info.ecv_i_slo & 0xFF00)>>8; |
| p_curr_cal = (fuelgauge->info.ecv_i_slo & 0x00FF); |
| |
| if (fuelgauge->info.en_high_fg_temp_cal && (fg_temp_gap > 0)) { |
| p_fg_delta_cal = (fg_temp_gap / fuelgauge->info.high_fg_temp_p_cal_denom)*fuelgauge->info.high_fg_temp_p_cal_fact; |
| n_fg_delta_cal = (fg_temp_gap / fuelgauge->info.high_fg_temp_n_cal_denom)*fuelgauge->info.high_fg_temp_n_cal_fact; |
| } else if (fuelgauge->info.en_low_fg_temp_cal && (fg_temp_gap < 0)) { |
| fg_temp_gap = -fg_temp_gap; |
| p_fg_delta_cal = (fg_temp_gap / fuelgauge->info.low_fg_temp_p_cal_denom)*fuelgauge->info.low_fg_temp_p_cal_fact; |
| n_fg_delta_cal = (fg_temp_gap / fuelgauge->info.low_fg_temp_n_cal_denom)*fuelgauge->info.low_fg_temp_n_cal_fact; |
| } |
| p_curr_cal = p_curr_cal + (p_fg_delta_cal); |
| n_curr_cal = n_curr_cal + (n_fg_delta_cal); |
| |
| pr_info("%s: <%d %d %d %d %d %d %d %d %d %d>, temp_fg = %d ,p_curr_cal = 0x%x, n_curr_cal = 0x%x, " |
| "batt_temp = %d\n", |
| __func__, |
| fuelgauge->info.en_high_fg_temp_cal, |
| fuelgauge->info.high_fg_temp_p_cal_denom, fuelgauge->info.high_fg_temp_p_cal_fact, |
| fuelgauge->info.high_fg_temp_n_cal_denom, fuelgauge->info.high_fg_temp_n_cal_fact, |
| fuelgauge->info.en_low_fg_temp_cal, |
| fuelgauge->info.low_fg_temp_p_cal_denom, fuelgauge->info.low_fg_temp_p_cal_fact, |
| fuelgauge->info.low_fg_temp_n_cal_denom, fuelgauge->info.low_fg_temp_n_cal_fact, |
| fuelgauge->info.temp_fg, p_curr_cal, n_curr_cal, fuelgauge->info.temperature); |
| |
| temp_gap = (fuelgauge->info.temperature/10) - fuelgauge->info.temp_std; |
| if (fuelgauge->info.en_high_temp_cal && (temp_gap > 0)) { |
| p_delta_cal = (temp_gap / fuelgauge->info.high_temp_p_cal_denom)*fuelgauge->info.high_temp_p_cal_fact; |
| n_delta_cal = (temp_gap / fuelgauge->info.high_temp_n_cal_denom)*fuelgauge->info.high_temp_n_cal_fact; |
| } else if (fuelgauge->info.en_low_temp_cal && (temp_gap < 0)) { |
| temp_gap = -temp_gap; |
| p_delta_cal = (temp_gap / fuelgauge->info.low_temp_p_cal_denom)*fuelgauge->info.low_temp_p_cal_fact; |
| n_delta_cal = (temp_gap / fuelgauge->info.low_temp_n_cal_denom)*fuelgauge->info.low_temp_n_cal_fact; |
| } |
| p_curr_cal = p_curr_cal + (p_delta_cal); |
| n_curr_cal = n_curr_cal + (n_delta_cal); |
| |
| curr_cal = (n_curr_cal << 8) | p_curr_cal; |
| |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_EV_I_SLO, curr_cal); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_CSP_I_SLO, curr_cal); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_CSN_I_SLO, curr_cal); |
| |
| pr_info("%s: <%d %d %d %d %d %d %d %d %d %d>, " |
| "p_curr_cal = 0x%x, n_curr_cal = 0x%x, mix_factor=0x%x ,curr_cal = 0x%x\n", |
| __func__, |
| fuelgauge->info.en_high_temp_cal, |
| fuelgauge->info.high_temp_p_cal_denom, fuelgauge->info.high_temp_p_cal_fact, |
| fuelgauge->info.high_temp_n_cal_denom, fuelgauge->info.high_temp_n_cal_fact, |
| fuelgauge->info.en_low_temp_cal, |
| fuelgauge->info.low_temp_p_cal_denom, fuelgauge->info.low_temp_p_cal_fact, |
| fuelgauge->info.low_temp_n_cal_denom, fuelgauge->info.low_temp_n_cal_fact, |
| p_curr_cal, n_curr_cal, mix_factor, curr_cal); |
| |
| return; |
| } |
| |
| static int sm5713_fg_verified_write_word(struct i2c_client *client, |
| u8 reg_addr, u16 data) |
| { |
| int ret; |
| |
| ret = sm5713_write_word(client, reg_addr, data); |
| if (ret < 0) { |
| msleep(50); |
| pr_info("1st fail i2c write %s: ret = %d, addr = 0x%x, data = 0x%x\n", |
| __func__, ret, reg_addr, data); |
| ret = sm5713_write_word(client, reg_addr, data); |
| if (ret < 0) { |
| msleep(50); |
| pr_info("2nd fail i2c write %s: ret = %d, addr = 0x%x, data = 0x%x\n", |
| __func__, ret, reg_addr, data); |
| ret = sm5713_write_word(client, reg_addr, data); |
| if (ret < 0) { |
| pr_info("3rd fail i2c write %s: ret = %d, addr = 0x%x, data = 0x%x\n", |
| __func__, ret, reg_addr, data); |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| static int sm5713_fg_fs_read_word_table(struct i2c_client *client, |
| u8 reg_addr, u8 count) |
| { |
| int ret, i; |
| |
| for (i = 0; i < count; i++) { |
| ret = sm5713_read_word(client, reg_addr); |
| if (ret < 0) { |
| pr_err("%s: 1st fail i2c write ret = %d, addr = 0x%x\n, count = %d", |
| __func__, ret, reg_addr, count); |
| } else { |
| if (ret == 0xffff) |
| ret = sm5713_read_word(client, reg_addr); |
| else |
| break; |
| } |
| } |
| return ret; |
| } |
| |
| int sm5713_fg_calculate_iocv(struct sm5713_fuelgauge_data *fuelgauge, bool is_vsys) |
| { |
| bool only_lb = false, sign_i_offset = 0; /*valid_cb=false, */ |
| int roop_start = 0, roop_max = 0, i = 0, cb_last_index = 0, cb_pre_last_index = 0; |
| int lb_v_buffer[FG_INIT_B_LEN+1] = {0, 0, 0, 0, 0, 0, 0, 0}; |
| int lb_i_buffer[FG_INIT_B_LEN+1] = {0, 0, 0, 0, 0, 0, 0, 0}; |
| int cb_v_buffer[FG_INIT_B_LEN+1] = {0, 0, 0, 0, 0, 0, 0, 0}; |
| int cb_i_buffer[FG_INIT_B_LEN+1] = {0, 0, 0, 0, 0, 0, 0, 0}; |
| int i_offset_margin = 0x14, i_vset_margin = 0x67; |
| int v_max = 0, v_min = 0, v_sum = 0, lb_v_avg = 0, cb_v_avg = 0, lb_v_set = 0, lb_i_set = 0, i_offset = 0; /* lb_v_minmax_offset=0, */ |
| int i_max = 0, i_min = 0, i_sum = 0, lb_i_avg = 0, cb_i_avg = 0, cb_v_set = 0, cb_i_set = 0; /* lb_i_minmax_offset=0, */ |
| int lb_i_p_v_min = 0, lb_i_n_v_max = 0, cb_i_p_v_min = 0, cb_i_n_v_max = 0; |
| |
| int v_ret = 0, i_ret = 0, ret = 0; |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_END_V_IDX); |
| pr_info("%s: iocv_status_read = addr : 0x%x , data : 0x%x\n", __func__, SM5713_FG_REG_END_V_IDX, ret); |
| |
| /* init start */ |
| if ((ret & 0x0010) == 0x0000) { |
| only_lb = true; |
| } |
| |
| /* |
| if ((ret & 0x0300) == 0x0300) { |
| valid_cb = true; |
| } |
| */ |
| /* init end */ |
| |
| /* lb get start */ |
| roop_max = (ret & 0x000F); |
| if (roop_max > FG_INIT_B_LEN) |
| roop_max = FG_INIT_B_LEN; |
| |
| roop_start = SM5713_FG_REG_START_LB_V; |
| for (i = roop_start; i < roop_start + roop_max; i++) { |
| if (is_vsys) |
| v_ret = sm5713_read_word(fuelgauge->i2c, i+0x10); |
| else |
| v_ret = sm5713_read_word(fuelgauge->i2c, i); |
| |
| i_ret = sm5713_read_word(fuelgauge->i2c, i+0x20); |
| |
| if ((i_ret&0x4000) == 0x4000) { |
| i_ret = -(i_ret&0x3FFF); |
| } |
| |
| lb_v_buffer[i-roop_start] = v_ret; |
| lb_i_buffer[i-roop_start] = i_ret; |
| |
| if (i == roop_start) { |
| v_max = v_ret; |
| v_min = v_ret; |
| v_sum = v_ret; |
| i_max = i_ret; |
| i_min = i_ret; |
| i_sum = i_ret; |
| } else { |
| if (v_ret > v_max) |
| v_max = v_ret; |
| else if (v_ret < v_min) |
| v_min = v_ret; |
| v_sum = v_sum + v_ret; |
| |
| if (i_ret > i_max) |
| i_max = i_ret; |
| else if (i_ret < i_min) |
| i_min = i_ret; |
| i_sum = i_sum + i_ret; |
| } |
| |
| if (abs(i_ret) > i_vset_margin) { |
| if (i_ret > 0) { |
| if (lb_i_p_v_min == 0) { |
| lb_i_p_v_min = v_ret; |
| } else { |
| if (v_ret < lb_i_p_v_min) |
| lb_i_p_v_min = v_ret; |
| } |
| } else { |
| if (lb_i_n_v_max == 0) { |
| lb_i_n_v_max = v_ret; |
| } else { |
| if (v_ret > lb_i_n_v_max) |
| lb_i_n_v_max = v_ret; |
| } |
| } |
| } |
| } |
| v_sum = v_sum - v_max - v_min; |
| i_sum = i_sum - i_max - i_min; |
| |
| /* |
| lb_v_minmax_offset = v_max - v_min; |
| lb_i_minmax_offset = i_max - i_min; |
| */ |
| |
| lb_v_avg = v_sum / (roop_max-2); |
| lb_i_avg = i_sum / (roop_max-2); |
| /* lb get end */ |
| |
| /* lb_vset start */ |
| if (abs(lb_i_buffer[roop_max-1]) < i_vset_margin) { |
| if (abs(lb_i_buffer[roop_max-2]) < i_vset_margin) { |
| lb_v_set = MAXVAL(lb_v_buffer[roop_max-2], lb_v_buffer[roop_max-1]); |
| if (abs(lb_i_buffer[roop_max-3]) < i_vset_margin) { |
| lb_v_set = MAXVAL(lb_v_buffer[roop_max-3], lb_v_set); |
| } |
| } else { |
| lb_v_set = lb_v_buffer[roop_max-1]; |
| } |
| } else { |
| lb_v_set = lb_v_avg; |
| } |
| |
| if (lb_i_n_v_max > 0) { |
| lb_v_set = MAXVAL(lb_i_n_v_max, lb_v_set); |
| } |
| /* |
| else if (lb_i_p_v_min > 0) { |
| lb_v_set = MINVAL(lb_i_p_v_min, lb_v_set); |
| } |
| lb_vset end |
| |
| lb offset make start |
| */ |
| if (roop_max > 3) { |
| lb_i_set = (lb_i_buffer[2] + lb_i_buffer[3]) / 2; |
| } |
| |
| if ((abs(lb_i_buffer[roop_max-1]) < i_offset_margin) && (abs(lb_i_set) < i_offset_margin)) { |
| lb_i_set = MAXVAL(lb_i_buffer[roop_max-1], lb_i_set); |
| } else if (abs(lb_i_buffer[roop_max-1]) < i_offset_margin) { |
| lb_i_set = lb_i_buffer[roop_max-1]; |
| } else if (abs(lb_i_set) < i_offset_margin) { |
| i_offset = lb_i_set; |
| } else { |
| lb_i_set = 0; |
| } |
| |
| i_offset = lb_i_set; |
| |
| i_offset = i_offset + 4; /* add extra offset */ |
| |
| if (i_offset <= 0) { |
| sign_i_offset = 1; |
| #ifdef IGNORE_N_I_OFFSET |
| i_offset = 0; |
| #else |
| i_offset = -i_offset; |
| #endif |
| } |
| |
| i_offset = i_offset>>1; |
| |
| if (sign_i_offset == 0) { |
| i_offset = i_offset|0x0080; |
| } |
| i_offset = i_offset | i_offset<<8; |
| /* |
| do not write in kernel point. |
| sm5713_write_word(client, SM5713_FG_REG_DP_ECV_I_OFF, i_offset); |
| lb offset make end |
| */ |
| pr_info("%s: iocv_l_max=0x%x, iocv_l_min=0x%x, iocv_l_avg=0x%x, lb_v_set=0x%x, roop_max=%d \n", |
| __func__, v_max, v_min, lb_v_avg, lb_v_set, roop_max); |
| pr_info("%s: ioci_l_max=0x%x, ioci_l_min=0x%x, ioci_l_avg=0x%x, lb_i_set=0x%x, i_offset=0x%x, sign_i_offset=%d\n", |
| __func__, i_max, i_min, lb_i_avg, lb_i_set, i_offset, sign_i_offset); |
| |
| if (!only_lb) { |
| /* cb get start */ |
| roop_start = SM5713_FG_REG_START_CB_V; |
| roop_max = 6; |
| for (i = roop_start; i < roop_start + roop_max; i++) { |
| |
| if (is_vsys) |
| v_ret = sm5713_read_word(fuelgauge->i2c, i+0x10); |
| else |
| v_ret = sm5713_read_word(fuelgauge->i2c, i); |
| i_ret = sm5713_read_word(fuelgauge->i2c, i+0x20); |
| |
| if ((i_ret&0x4000) == 0x4000) { |
| i_ret = -(i_ret&0x3FFF); |
| } |
| |
| cb_v_buffer[i-roop_start] = v_ret; |
| cb_i_buffer[i-roop_start] = i_ret; |
| |
| if (i == roop_start) { |
| v_max = v_ret; |
| v_min = v_ret; |
| v_sum = v_ret; |
| i_max = i_ret; |
| i_min = i_ret; |
| i_sum = i_ret; |
| } else { |
| if (v_ret > v_max) |
| v_max = v_ret; |
| else if (v_ret < v_min) |
| v_min = v_ret; |
| v_sum = v_sum + v_ret; |
| |
| if (i_ret > i_max) |
| i_max = i_ret; |
| else if (i_ret < i_min) |
| i_min = i_ret; |
| i_sum = i_sum + i_ret; |
| } |
| |
| if (abs(i_ret) > i_vset_margin) { |
| if (i_ret > 0) { |
| if (cb_i_p_v_min == 0) { |
| cb_i_p_v_min = v_ret; |
| } else { |
| if (v_ret < cb_i_p_v_min) |
| cb_i_p_v_min = v_ret; |
| } |
| } else { |
| if (cb_i_n_v_max == 0) { |
| cb_i_n_v_max = v_ret; |
| } else { |
| if (v_ret > cb_i_n_v_max) |
| cb_i_n_v_max = v_ret; |
| } |
| } |
| } |
| } |
| v_sum = v_sum - v_max - v_min; |
| i_sum = i_sum - i_max - i_min; |
| |
| cb_v_avg = v_sum / (roop_max-2); |
| cb_i_avg = i_sum / (roop_max-2); |
| /* cb get end */ |
| |
| /* cb_vset start */ |
| cb_last_index = (ret & 0x000F)-7; /*-6-1 */ |
| if (cb_last_index < 0) { |
| cb_last_index = 5; |
| } |
| |
| for (i = roop_max; i > 0; i--) { |
| if (abs(cb_i_buffer[cb_last_index]) < i_vset_margin) { |
| cb_v_set = cb_v_buffer[cb_last_index]; |
| if (abs(cb_i_buffer[cb_last_index]) < i_offset_margin) { |
| cb_i_set = cb_i_buffer[cb_last_index]; |
| } |
| |
| cb_pre_last_index = cb_last_index - 1; |
| if (cb_pre_last_index < 0) { |
| cb_pre_last_index = 5; |
| } |
| |
| if (abs(cb_i_buffer[cb_pre_last_index]) < i_vset_margin) { |
| cb_v_set = MAXVAL(cb_v_buffer[cb_pre_last_index], cb_v_set); |
| if (abs(cb_i_buffer[cb_pre_last_index]) < i_offset_margin) { |
| cb_i_set = MAXVAL(cb_i_buffer[cb_pre_last_index], cb_i_set); |
| } |
| } |
| } else { |
| cb_last_index--; |
| if (cb_last_index < 0) { |
| cb_last_index = 5; |
| } |
| } |
| } |
| |
| if (cb_v_set == 0) { |
| cb_v_set = cb_v_avg; |
| if (cb_i_set == 0) { |
| cb_i_set = cb_i_avg; |
| } |
| } |
| |
| if (cb_i_n_v_max > 0) { |
| cb_v_set = MAXVAL(cb_i_n_v_max, cb_v_set); |
| } |
| /* |
| else if(cb_i_p_v_min > 0) { |
| cb_v_set = MINVAL(cb_i_p_v_min, cb_v_set); |
| } |
| cb_vset end |
| |
| cb offset make start |
| */ |
| if (abs(cb_i_set) < i_offset_margin) { |
| if (cb_i_set > lb_i_set) { |
| i_offset = cb_i_set; |
| i_offset = i_offset + 4; /* add extra offset */ |
| |
| if (i_offset <= 0) { |
| sign_i_offset = 1; |
| #ifdef IGNORE_N_I_OFFSET |
| i_offset = 0; |
| #else |
| i_offset = -i_offset; |
| #endif |
| } |
| |
| i_offset = i_offset>>1; |
| |
| if (sign_i_offset == 0) { |
| i_offset = i_offset|0x0080; |
| } |
| i_offset = i_offset | i_offset<<8; |
| |
| /* do not write in kernel point. */ |
| /* sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DP_ECV_I_OFF, i_offset); */ |
| } |
| } |
| /* cb offset make end */ |
| |
| pr_info("%s: iocv_c_max=0x%x, iocv_c_min=0x%x, iocv_c_avg=0x%x, cb_v_set=0x%x, cb_last_index=%d, is_vsys=%d \n", |
| __func__, v_max, v_min, cb_v_avg, cb_v_set, cb_last_index, is_vsys); |
| pr_info("%s: ioci_c_max=0x%x, ioci_c_min=0x%x, ioci_c_avg=0x%x, cb_i_set=0x%x, i_offset=0x%x, sign_i_offset=%d\n", |
| __func__, i_max, i_min, cb_i_avg, cb_i_set, i_offset, sign_i_offset); |
| |
| } |
| |
| /* final set */ |
| if ((abs(cb_i_set) > i_vset_margin) || only_lb) { |
| ret = MAXVAL(lb_v_set, cb_i_n_v_max); |
| } else { |
| ret = cb_v_set; |
| } |
| |
| if (ret > fuelgauge->info.battery_table[DISCHARGE_TABLE][FG_TABLE_LEN-1]) { |
| pr_info("%s: iocv ret change 0x%x -> 0x%x \n", __func__, ret, fuelgauge->info.battery_table[DISCHARGE_TABLE][FG_TABLE_LEN-1]); |
| ret = fuelgauge->info.battery_table[DISCHARGE_TABLE][FG_TABLE_LEN-1]; |
| } else if (ret < fuelgauge->info.battery_table[DISCHARGE_TABLE][0]) { |
| pr_info("%s: iocv ret change 0x%x -> 0x%x \n", __func__, ret, (fuelgauge->info.battery_table[DISCHARGE_TABLE][0] + 0x10)); |
| ret = fuelgauge->info.battery_table[DISCHARGE_TABLE][0] + 0x10; |
| } |
| |
| return ret; |
| } |
| |
| void sm5713_set_cycle_cfg(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int value; |
| |
| value = fuelgauge->info.cycle_limit_cntl|(fuelgauge->info.cycle_high_limit<<12)|(fuelgauge->info.cycle_low_limit<<8); |
| |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_SOC_CYCLE_CFG, value); |
| |
| pr_info("%s: cycle cfg value = 0x%x\n", __func__, value); |
| |
| } |
| |
| void sm5713_set_arsm_cfg(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int value; |
| |
| value = fuelgauge->info.arsm[0]<<15 | fuelgauge->info.arsm[1]<<6 | fuelgauge->info.arsm[2]<<4 | fuelgauge->info.arsm[3]; |
| |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_AUTO_RS_MAN, value); |
| |
| pr_info("%s: arsm cfg value = 0x%x\n", __func__, value); |
| } |
| |
| #ifdef ENABLE_BATT_LONG_LIFE |
| int get_v_max_index_by_cycle(struct sm5713_fuelgauge_data *fuelgauge) |
| |
| { |
| int cycle_index = 0, len; |
| |
| for (len = fuelgauge->pdata->num_age_step-1; len >= 0; --len) { |
| if (fuelgauge->chg_full_soc == fuelgauge->pdata->age_data[len].full_condition_soc) { |
| cycle_index = len; |
| break; |
| } |
| } |
| pr_info("%s: chg_full_soc = %d, index = %d \n", __func__, fuelgauge->chg_full_soc, cycle_index); |
| |
| return cycle_index; |
| } |
| #endif |
| |
| |
| static bool sm5713_fg_reg_init(struct sm5713_fuelgauge_data *fuelgauge, bool is_surge) |
| { |
| int i, j, k, value, ret = 0; |
| uint8_t table_reg; |
| int write_table[TABLE_MAX][FG_TABLE_LEN+1]; |
| int error_remain = 0, error_check = 0; |
| |
| pr_info("%s: sm5713_fg_reg_init START!!\n", __func__); |
| |
| /* init mark */ |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RESET, FG_INIT_MARK); |
| |
| /* start first param_ctrl unlock */ |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_PARAM_CTRL, FG_PARAM_UNLOCK_CODE); |
| |
| /* RCE write */ |
| for (i = 0; i < 3; i++) { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RCE0+i, fuelgauge->info.rce_value[i]); |
| pr_info("%s: RCE write RCE%d = 0x%x : 0x%x\n", |
| __func__, i, SM5713_FG_REG_RCE0+i, fuelgauge->info.rce_value[i]); |
| } |
| |
| /* DTCD write */ |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_DTCD, fuelgauge->info.dtcd_value); |
| pr_info("%s: DTCD write DTCD = 0x%x : 0x%x\n", |
| __func__, SM5713_FG_REG_DTCD, fuelgauge->info.dtcd_value); |
| |
| /* RS write */ |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MAN, fuelgauge->info.rs_value[0]); |
| pr_info("%s: RS write RS = 0x%x : 0x%x\n", |
| __func__, SM5713_FG_REG_AUTO_RS_MAN, fuelgauge->info.rs_value[0]); |
| |
| /* VIT_PERIOD write */ |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_VIT_PERIOD, fuelgauge->info.vit_period); |
| pr_info("%s: VIT_PERIOD write VIT_PERIOD = 0x%x : 0x%x\n", |
| __func__, SM5713_FG_REG_VIT_PERIOD, fuelgauge->info.vit_period); |
| |
| /* TABLE_LEN write & pram unlock */ |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_PARAM_CTRL, |
| FG_PARAM_UNLOCK_CODE | FG_TABLE_LEN); |
| /* CAP write */ |
| #ifdef ENABLE_BATT_LONG_LIFE |
| i = get_v_max_index_by_cycle(fuelgauge); |
| pr_info("%s: v_max_now is change %x -> %x \n", __func__, fuelgauge->info.v_max_now, fuelgauge->info.v_max_table[i]); |
| pr_info("%s: q_max_now is change %x -> %x \n", __func__, fuelgauge->info.q_max_now, fuelgauge->info.q_max_table[i]); |
| fuelgauge->info.v_max_now = fuelgauge->info.v_max_table[i]; |
| fuelgauge->info.q_max_now = fuelgauge->info.q_max_table[i]; |
| fuelgauge->info.cap = fuelgauge->info.q_max_now; |
| #endif |
| |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_BAT_CAP, fuelgauge->info.cap); |
| pr_info("%s: SM5713_REG_CAP 0x%x, 0x%x\n", |
| __func__, fuelgauge->info.min_cap, fuelgauge->info.cap); |
| |
| for (i = 0; i < TABLE_MAX; i++) { |
| for (j = 0; j <= FG_TABLE_LEN; j++) { |
| #ifdef ENABLE_BATT_LONG_LIFE |
| if (i == SOC_TABLE) { |
| write_table[i][j] = (fuelgauge->info.battery_table[i][j] * 2) - (fuelgauge->info.battery_table[i][j] * fuelgauge->info.q_max_now / fuelgauge->info.maxcap); |
| if (j == FG_TABLE_LEN) { |
| write_table[i][FG_TABLE_LEN-1] = 100*256; |
| write_table[i][FG_TABLE_LEN] = 100*256+26; |
| } |
| } else { |
| write_table[i][j] = fuelgauge->info.battery_table[i][j]; |
| if (j == FG_TABLE_LEN-1){ |
| write_table[i][FG_TABLE_LEN-1] = fuelgauge->info.v_max_now; |
| if (write_table[i][FG_TABLE_LEN-1] < write_table[i][FG_TABLE_LEN-2]) { |
| write_table[i][FG_TABLE_LEN-2] = write_table[i][FG_TABLE_LEN-1] - 0x18; // ~11.7mV |
| write_table[SOC_TABLE][FG_TABLE_LEN-2] = (write_table[SOC_TABLE][FG_TABLE_LEN-1]*99)/100; |
| } |
| } |
| } |
| #else |
| write_table[i][j] = fuelgauge->info.battery_table[i][j]; |
| #endif |
| } |
| } |
| |
| for (i = 0; i < TABLE_MAX; i++) { |
| table_reg = SM5713_FG_REG_TABLE_0_START + (i*(FG_TABLE_LEN+1)); |
| for (j = 0; j <= FG_TABLE_LEN; j++) { |
| sm5713_write_word(fuelgauge->i2c, (table_reg + j), write_table[i][j]); |
| msleep(10); |
| value = sm5713_fg_fs_read_word_table(fuelgauge->i2c, |
| (table_reg + j), TABLE_READ_COUNT); |
| if (write_table[i][j] == value) { |
| pr_info("%s: TABLE write and verify OK [%d][%d] = 0x%x : 0x%x\n", |
| __func__, i, j, (table_reg + j), write_table[i][j]); |
| } else { |
| error_check = 1; |
| |
| for (k = 1; k <= I2C_ERROR_COUNT_MAX; k++) { |
| pr_info("%s: TABLE write data ERROR!!!! rewrite [%d][%d] = 0x%x : 0x%x, count=%d\n", |
| __func__, i, j, (table_reg + j), write_table[i][j], k); |
| sm5713_write_word(fuelgauge->i2c, (table_reg + j), write_table[i][j]); |
| msleep(30); |
| value = sm5713_fg_fs_read_word_table(fuelgauge->i2c, |
| (table_reg + j), TABLE_READ_COUNT); |
| if (write_table[i][j] == value) { |
| pr_info("%s: TABLE rewrite OK [%d][%d] = 0x%x : 0x%x, count=%d\n", |
| __func__, i, j, (table_reg + j), write_table[i][j], k); |
| break; |
| } |
| |
| if (k == I2C_ERROR_COUNT_MAX) |
| error_remain = 1; |
| } |
| } |
| } |
| } |
| |
| table_reg = SM5713_FG_REG_TABLE_2_START; |
| for (j = 0; j <= FG_ADD_TABLE_LEN; j++) { |
| sm5713_write_word(fuelgauge->i2c, (table_reg + j), fuelgauge->info.battery_table[i][j]); |
| pr_info("%s: TABLE write OK [%d][%d] = 0x%x : 0x%x\n", |
| __func__, i, j, (table_reg + j), fuelgauge->info.battery_table[i][j]); |
| } |
| |
| /* MIX_MODE write */ |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MIX_FACTOR, fuelgauge->info.rs_value[2]); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MAX, fuelgauge->info.rs_value[3]); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_RS_MIN, fuelgauge->info.rs_value[4]); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_MIX_RATE, fuelgauge->info.mix_value[0]); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_MIX_INIT_BLANK, fuelgauge->info.mix_value[1]); |
| |
| pr_info("%s: RS_MIX_FACTOR = 0x%x, RS_MAX = 0x%x, RS_MIN = 0x%x, MIX_RATE = 0x%x, MIX_INIT_BLANK = 0x%x\n", |
| __func__, |
| fuelgauge->info.rs_value[2], fuelgauge->info.rs_value[3], fuelgauge->info.rs_value[4], |
| fuelgauge->info.mix_value[0], fuelgauge->info.mix_value[1]); |
| |
| /* v_cal write */ |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_VOLT_CAL, fuelgauge->info.volt_cal[0]); |
| /* need writing value print for debug */ |
| |
| /* MISC write */ |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_MISC, fuelgauge->info.misc); |
| pr_info("%s: SM5713_REG_MISC 0x%x : 0x%x\n", |
| __func__, SM5713_FG_REG_MISC, fuelgauge->info.misc); |
| |
| /* TOPOFF SOC */ |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_TOPOFF_SOC, fuelgauge->info.topoff_soc); |
| pr_info("%s: SM5713_REG_TOPOFF_SOC 0x%x : 0x%x\n", __func__, |
| SM5713_FG_REG_TOPOFF_SOC, fuelgauge->info.topoff_soc); |
| |
| /* INIT_last - control register set */ |
| value = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_CNTL); |
| if (value == CNTL_REG_DEFAULT_VALUE) { |
| value = fuelgauge->info.cntl_value; |
| } |
| value = ENABLE_MIX_MODE | ENABLE_TEMP_MEASURE | ENABLE_MANUAL_OCV | (fuelgauge->info.enable_topoff_soc << 13); |
| pr_info("%s: SM5713_REG_CNTL reg : 0x%x\n", __func__, value); |
| |
| ret = sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_CNTL, value); |
| if (ret < 0) |
| pr_info("%s: fail control register set(%d)\n", __func__, ret); |
| |
| pr_info("%s: LAST SM5713_REG_CNTL = 0x%x : 0x%x\n", __func__, SM5713_FG_REG_CNTL, value); |
| |
| /* LOCK */ |
| value = FG_PARAM_LOCK_CODE | FG_TABLE_LEN; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_PARAM_CTRL, value); |
| pr_info("%s: LAST PARAM CTRL VALUE = 0x%x : 0x%x\n", __func__, SM5713_FG_REG_PARAM_CTRL, value); |
| |
| /* surge reset defence */ |
| if (is_surge) { |
| value = ((fuelgauge->info.batt_ocv<<8)/125); |
| } else { |
| if ((sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_AUX_STAT) & 0x0002) || |
| factory_mode) { |
| value = sm5713_fg_calculate_iocv(fuelgauge, true); |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_1); |
| ret &= ~JIG_CONNECTED; |
| ret |= JIG_CONNECTED; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_1, ret); |
| } else |
| value = sm5713_fg_calculate_iocv(fuelgauge, false); |
| |
| if ((fuelgauge->info.volt_cal[0] & 0x0080) == 0x0080) { |
| value = value - (fuelgauge->info.volt_cal[0] & 0x007F); |
| } else { |
| value = value + (fuelgauge->info.volt_cal[0] & 0x007F); |
| } |
| } |
| |
| msleep(10); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_IOCV_MAN, value); |
| pr_info("%s: IOCV_MAN_WRITE = %d : 0x%x\n", __func__, SM5713_FG_REG_IOCV_MAN, value); |
| |
| /* init delay */ |
| msleep(20); |
| |
| /* write cycle cfg */ |
| sm5713_set_cycle_cfg(fuelgauge); |
| /* write auto_rs_man cfg */ |
| sm5713_set_arsm_cfg(fuelgauge); |
| |
| /* write batt data version */ |
| value = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_1); |
| value &= ~DATA_VERSION; |
| value |= (fuelgauge->info.data_ver << 4) & DATA_VERSION; |
| if (error_remain) |
| value |= I2C_ERROR_REMAIN; |
| if (error_check) |
| value |= I2C_ERROR_CHECK; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_1, value); |
| pr_info("%s: RESERVED = %d : 0x%x\n", __func__, SM5713_FG_REG_USER_RESERV_1, value); |
| |
| return 1; |
| } |
| |
| static int sm5713_abnormal_reset_check(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int cntl_read, reset_read, table_len_read; |
| |
| reset_read = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_RESET) & 0xF000; |
| table_len_read = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_PARAM_CTRL) & 0x001F; |
| |
| /* abnormal case process */ |
| if (sm5713_fg_check_reg_init_need(fuelgauge) || (reset_read == 0) || (table_len_read != FG_TABLE_LEN)) { |
| cntl_read = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_CNTL); |
| pr_info("%s: SM5713 FG abnormal case!!!! SM5713_REG_CNTL : 0x%x, is_FG_initialised : %d, reset_read : 0x%x, table_len_read : 0x%x\n", |
| __func__, cntl_read, fuelgauge->info.is_FG_initialised, reset_read, table_len_read); |
| |
| if (fuelgauge->info.is_FG_initialised == 1) { |
| /* SW reset code */ |
| fuelgauge->info.is_FG_initialised = 0; |
| if (sm5713_fg_verified_write_word(fuelgauge->i2c, SM5713_FG_REG_RESET, SW_RESET_OTP_CODE) < 0) { |
| pr_info("%s: Warning!!!! SM5713 FG abnormal case.... SW reset FAIL \n", __func__); |
| } else { |
| pr_info("%s: SM5713 FG abnormal case.... SW reset OK\n", __func__); |
| } |
| /* delay 100ms */ |
| msleep(100); |
| #ifdef ENABLE_SM5713_MQ_FUNCTION |
| sm5713_meas_mq_off(fuelgauge); |
| #endif |
| /* init code */ |
| sm5713_fg_init(fuelgauge, true); |
| } |
| return FG_ABNORMAL_RESET; |
| } |
| return 0; |
| } |
| |
| static unsigned int sm5713_get_device_id(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret; |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_DEVICE_ID); |
| sm5713_device_id = ret; |
| pr_info("%s: SM5713 device_id = 0x%x\n", __func__, ret); |
| |
| return ret; |
| } |
| |
| int sm5713_call_fg_device_id(void) |
| { |
| pr_info("%s: extern call SM5713 fg_device_id = 0x%x\n", __func__, sm5713_device_id); |
| |
| return sm5713_device_id; |
| } |
| |
| unsigned int sm5713_get_soc(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret; |
| unsigned int soc; |
| |
| |
| |
| pr_info("%s: \n", __func__); |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_SOC); |
| if (ret < 0) { |
| pr_err("%s: Warning!!!! read soc reg fail\n", __func__); |
| soc = 500; |
| } else { |
| soc = ((ret&0x7f00)>>8) * 10; /* integer bit; */ |
| soc = soc + (((ret&0x00ff)*10)/256); /* integer + fractional bit */ |
| } |
| |
| if (ret&0x8000) { |
| soc = 0; |
| } |
| /* soc = 500; */ |
| |
| if (sm5713_abnormal_reset_check(fuelgauge) < 0) { |
| pr_info("%s: FG init ERROR!! pre_SOC returned!!, read_SOC = %d, pre_SOC = %d\n", __func__, soc, fuelgauge->info.batt_soc); |
| return fuelgauge->info.batt_soc; |
| } |
| |
| /* ocv update */ |
| sm5713_get_ocv(fuelgauge); |
| |
| #ifdef SM5713_FG_FULL_DEBUG |
| pr_info("%s: read = 0x%x, soc = %d\n", __func__, ret, soc); |
| #endif |
| |
| /* for low temp power off test */ |
| if (fuelgauge->info.volt_alert_flag && (fuelgauge->info.temperature < -100)) { |
| pr_info("%s: volt_alert_flag is TRUE!!!! SOC make force ZERO!!!!\n", __func__); |
| fuelgauge->info.batt_soc = 0; |
| return 0; |
| } else { |
| fuelgauge->info.batt_soc = soc; |
| pr_info("%s: batt_soc = %d, soc = %d\n", __func__, fuelgauge->info.batt_soc, soc); |
| } |
| |
| return soc; |
| } |
| |
| static void sm5713_fg_test_read(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret0, ret1, ret2, ret3, ret4, ret5, ret6, ret7, ret8, ret9, t_addr; |
| |
| t_addr = SM5713_FG_REG_TABLE_0_START + (FG_TABLE_LEN+1); |
| |
| ret0 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_TABLE_0_START); |
| ret1 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_TABLE_0_START+FG_TABLE_LEN-3); |
| ret2 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_TABLE_0_START+FG_TABLE_LEN-2); |
| ret3 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_TABLE_0_START+FG_TABLE_LEN-1); |
| ret4 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_TABLE_0_START+FG_TABLE_LEN); |
| ret5 = sm5713_read_word(fuelgauge->i2c, 0x28); |
| ret6 = sm5713_read_word(fuelgauge->i2c, 0x2F); |
| ret7 = sm5713_read_word(fuelgauge->i2c, 0x01); |
| pr_info("%s: 0xA0=0x%04x, 0xAC=0x%04x, 0xAD=0x%04x, 0xAE=0x%04x, 0xAF=0x%04x, 0x28=0x%04x, 0x2F=0x%04x, 0x01=0x%04x, SM5713_ID=0x%04x\n", |
| __func__, ret0, ret1, ret2, ret3, ret4, ret5, ret6, ret7, sm5713_device_id); |
| |
| ret0 = sm5713_read_word(fuelgauge->i2c, t_addr); |
| ret1 = sm5713_read_word(fuelgauge->i2c, t_addr+FG_TABLE_LEN-3); |
| ret2 = sm5713_read_word(fuelgauge->i2c, t_addr+FG_TABLE_LEN-2); |
| ret3 = sm5713_read_word(fuelgauge->i2c, t_addr+FG_TABLE_LEN-1); |
| ret4 = sm5713_read_word(fuelgauge->i2c, t_addr+FG_TABLE_LEN); |
| ret5 = sm5713_read_word(fuelgauge->i2c, 0x85); |
| ret6 = sm5713_read_word(fuelgauge->i2c, 0x86); |
| ret7 = sm5713_read_word(fuelgauge->i2c, 0x87); |
| ret8 = sm5713_read_word(fuelgauge->i2c, 0x1F); |
| ret9 = sm5713_read_word(fuelgauge->i2c, 0x94); |
| pr_info("%s: 0xB0=0x%04x, 0xBC=0x%04x, 0xBD=0x%04x, 0xBE=0x%04x, 0xBF=0x%04x, 0x85=0x%04x, 0x86=0x%04x, 0x87=0x%04x, 0x1F=0x%04x, 0x94=0x%04x\n", |
| __func__, ret0, ret1, ret2, ret3, ret4, ret5, ret6, ret7, ret8, ret9); |
| |
| return; |
| } |
| |
| static void sm5713_update_all_value(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| |
| union power_supply_propval value; |
| int temp; |
| |
| fuelgauge->is_charging = (fuelgauge->info.flag_charge_health | |
| fuelgauge->ta_exist) && (fuelgauge->info.batt_current >= 30); |
| |
| /* check charger status */ |
| |
| psy_do_property("sm5713-charger", get, |
| POWER_SUPPLY_PROP_STATUS, value); |
| fuelgauge->info.flag_full_charge = |
| (value.intval == POWER_SUPPLY_STATUS_FULL) ? 1 : 0; |
| fuelgauge->info.flag_chg_status = |
| (value.intval == POWER_SUPPLY_STATUS_CHARGING) ? 1 : 0; |
| |
| /* vbat */ |
| sm5713_get_vbat(fuelgauge); |
| |
| /* current */ |
| sm5713_get_curr(fuelgauge); |
| |
| /* temperature */ |
| sm5713_get_temperature(fuelgauge); |
| |
| /* cycle */ |
| sm5713_get_cycle(fuelgauge); |
| |
| /* carc */ |
| sm5713_cal_carc(fuelgauge); |
| |
| /* soc */ |
| sm5713_get_soc(fuelgauge); |
| |
| |
| sm5713_fg_test_read(fuelgauge); |
| |
| pr_info("%s: chg_h=%d, chg_f=%d, chg_s=%d, is_chg=%d, ta_exist=%d, " |
| "v=%d, v_avg=%d, i=%d, i_avg=%d, ocv=%d, fg_t=%d, b_t=%d, cycle=%d, soc=%d, state=0x%x\n", |
| __func__, fuelgauge->info.flag_charge_health, fuelgauge->info.flag_full_charge, |
| fuelgauge->info.flag_chg_status, fuelgauge->is_charging, fuelgauge->ta_exist, |
| fuelgauge->info.batt_voltage, fuelgauge->info.batt_avgvoltage, |
| fuelgauge->info.batt_current, fuelgauge->info.batt_avgcurrent, fuelgauge->info.batt_ocv, |
| fuelgauge->info.temp_fg, fuelgauge->info.temperature, fuelgauge->info.batt_soc_cycle, |
| fuelgauge->info.batt_soc, sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_OCV_STATE)); |
| |
| #ifdef ENABLE_SM5713_MQ_FUNCTION |
| fuelgauge->info.full_mq_dump = sm5713_meas_mq_dump(fuelgauge); |
| #endif |
| |
| // for abnormal case asoc update stop, MUST USE AFTER PASSS_5 |
| if (fuelgauge->pmic_rev > 5) |
| { |
| if (fuelgauge->info.temperature/10 < 18) |
| { |
| temp = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_MISC); |
| temp = temp & !0x0040; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_MISC, temp); |
| } |
| else if ((fuelgauge->info.temperature/10 > fuelgauge->info.temp_std) && (fuelgauge->info.batt_soc > 950)) |
| { |
| temp = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_MISC); |
| temp = temp | 0x0040; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_MISC, temp); |
| } |
| pr_info("%s : temp = 0x%x, t = %d \n", __func__, temp, fuelgauge->info.temperature); |
| } |
| // for abnormal case asoc update stop, MUST USE AFTER PASSS_5 |
| } |
| |
| static int sm5713_fg_set_jig_mode_real_vbat(struct sm5713_fuelgauge_data *fuelgauge, int meas_mode) |
| { |
| int stat; |
| |
| fuelgauge->isjigmoderealvbat = false; |
| |
| if (sm5713_fg_check_reg_init_need(fuelgauge)) { |
| pr_info("%s: FG init fail!! \n", __func__); |
| return -1; |
| } |
| |
| /** meas_mode = 0 is inbat mode with jig **/ |
| if (meas_mode == SEC_BAT_INBAT_FGSRC_SWITCHING_VBAT) { |
| stat = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_AUX_STAT); |
| if (stat & 0x0002) { |
| fuelgauge->isjigmoderealvbat = true; |
| pr_info("%s: FG check jig_ON!! and after read real VBAT!! \n", __func__); |
| } else |
| pr_info("%s: isjigmoderealvbat = 1 but FG check nENQ4 LOW!! check jig!! \n", __func__); |
| } else |
| pr_info("%s: meas_mode = 1, isjigmoderealvbat = false!! \n", __func__); |
| |
| return 0; |
| } |
| |
| static int sm5713_fg_check_battery_present(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| /* SM5713 is not suport batt present */ |
| pr_info("%s: sm5713_fg_get_batt_present\n", __func__); |
| |
| return true; |
| } |
| |
| static bool sm5713_check_jig_status(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| bool ret = false; |
| |
| if (fuelgauge->pdata->jig_gpio) { |
| if (fuelgauge->pdata->jig_low_active) |
| ret = !gpio_get_value(fuelgauge->pdata->jig_gpio); |
| else |
| ret = gpio_get_value(fuelgauge->pdata->jig_gpio); |
| } |
| pr_info("%s: jig_gpio(%d), ret(%d)\n", |
| __func__, fuelgauge->pdata->jig_gpio, ret); |
| return ret; |
| } |
| |
| void sm5713_fg_reset_capacity_by_jig_connection(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret; |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_1); |
| ret &= ~JIG_CONNECTED; |
| ret |= JIG_CONNECTED; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_1, ret); |
| |
| pr_info("%s: set JIG_CONNECTED(0x%04x) (Jig Connection or bypass)\n", __func__, ret); |
| } |
| |
| int sm5713_fg_alert_init(struct sm5713_fuelgauge_data *fuelgauge, int soc) |
| { |
| int ret; |
| int value_soc_alarm; |
| |
| fuelgauge->is_fuel_alerted = false; |
| |
| /* remove interrupt */ |
| /* ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_INTFG); */ |
| |
| /* check status */ |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_STATUS); |
| if (ret < 0) { |
| pr_err("%s: Failed to read SM5713_FG_REG_STATUS\n", __func__); |
| return -1; |
| } |
| |
| /* remove all mask */ |
| /* sm5713_write_word(fuelgauge->i2c,SM5713_FG_REG_INTFG_MASK, 0x0000); */ |
| |
| /* enable volt alert only, other alert mask */ |
| /* |
| ret = MASK_L_SOC_INT|MASK_H_TEM_INT|MASK_L_TEM_INT; |
| sm5713_write_word(fuelgauge->i2c,SM5713_FG_REG_INTFG_MASK,ret); |
| fuelgauge->info.irq_ctrl = ~(ret); |
| */ |
| value_soc_alarm = (soc<<8); /* 0x0100 = 1.00% */ |
| if (sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_SOC_ALARM, value_soc_alarm) < 0) { |
| pr_err("%s: Failed to write SM5713_FG_REG_SOC_ALARM\n", __func__); |
| return -1; |
| } |
| |
| /* enabel volt alert control, other alert disable */ |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_CNTL); |
| if (ret < 0) { |
| pr_err("%s: Failed to read SM5713_FG_REG_CNTL\n", __func__); |
| return -1; |
| } |
| ret = ret | ENABLE_V_ALARM; |
| ret = ret & (~ENABLE_SOC_ALARM & ~ENABLE_T_H_ALARM & ~ENABLE_T_L_ALARM); |
| if (sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_CNTL, ret) < 0) { |
| pr_err("%s: Failed to write SM5713_REG_CNTL\n", __func__); |
| return -1; |
| } |
| |
| pr_info("%s: fg_irq= 0x%x, REG_CNTL=0x%x, SOC_ALARM=0x%x \n", |
| __func__, fuelgauge->fg_irq, ret, value_soc_alarm); |
| |
| return 1; |
| } |
| |
| static int sm5713_set_tcal_ioff(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int tcal, ioff; |
| |
| tcal = fuelgauge->info.tcal_ioff[0]; |
| ioff = fuelgauge->info.tcal_ioff[1]; |
| |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_VOLT_TEMP_CAL, tcal); |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_AUX_3, ioff); |
| pr_info("%s : set tcal & ioff!! tcal = 0x%x, ioff = 0x%x\n", __func__, tcal, ioff); |
| |
| return 0; |
| } |
| |
| static int sm5713_asoc_init(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret, temp; |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_AGING_CTRL); |
| pr_info("%s 0x%x : 0x%x\n", __func__, SM5713_FG_REG_AGING_CTRL, ret); |
| if(ret != fuelgauge->info.age_cntl){ |
| if (sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_AGING_CTRL, fuelgauge->info.age_cntl) < 0) { |
| pr_err("%s: Failed to write SM5713_FG_REG_AGING_CTRL\n", __func__); |
| } |
| } |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_2); |
| temp = ret; |
| ret = ret & 0x7F; |
| if (ret == 0) { |
| fuelgauge->info.soh = 100; |
| temp = temp & 0x80; |
| temp = temp | fuelgauge->info.soh; |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_2, temp); |
| pr_info("%s : soh reset %d, write 0x%x\n", __func__, fuelgauge->info.soh, temp); |
| } else { |
| fuelgauge->info.soh = ret; |
| pr_info("%s asoc restore : %d\n", __func__, fuelgauge->info.soh); |
| } |
| |
| return 0; |
| } |
| |
| static irqreturn_t sm5713_jig_irq_thread(int irq, void *irq_data) |
| { |
| struct sm5713_fuelgauge_data *fuelgauge = irq_data; |
| |
| if (sm5713_check_jig_status(fuelgauge)) |
| sm5713_fg_reset_capacity_by_jig_connection(fuelgauge); |
| else |
| pr_info("%s: jig removed\n", __func__); |
| return IRQ_HANDLED; |
| } |
| |
| static void sm5713_fg_buffer_read(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| int ret0, ret1, ret2, ret3, ret4, ret5; |
| |
| ret0 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_LB_V); |
| ret1 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_LB_V+1); |
| ret2 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_LB_V+2); |
| ret3 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_LB_V+3); |
| ret4 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_LB_V+4); |
| ret5 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_LB_V+5); |
| pr_info("%s: sm5713 FG buffer 0x30_0x35 lb_V = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n", |
| __func__, ret0, ret1, ret2, ret3, ret4, ret5); |
| |
| ret0 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_CB_V); |
| ret1 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_CB_V+1); |
| ret2 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_CB_V+2); |
| ret3 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_CB_V+3); |
| ret4 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_CB_V+4); |
| ret5 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_CB_V+5); |
| pr_info("%s: sm5713 FG buffer 0x36_0x3B cb_V = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n", |
| __func__, ret0, ret1, ret2, ret3, ret4, ret5); |
| |
| |
| ret0 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_LB_I); |
| ret1 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_LB_I+1); |
| ret2 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_LB_I+2); |
| ret3 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_LB_I+3); |
| ret4 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_LB_I+4); |
| ret5 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_LB_I+5); |
| pr_info("%s: sm5713 FG buffer 0x40_0x45 lb_I = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n", |
| __func__, ret0, ret1, ret2, ret3, ret4, ret5); |
| |
| |
| ret0 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_CB_I); |
| ret1 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_CB_I+1); |
| ret2 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_CB_I+2); |
| ret3 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_CB_I+3); |
| ret4 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_CB_I+4); |
| ret5 = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_START_CB_I+5); |
| pr_info("%s: sm5713 FG buffer 0x46_0x4B cb_I = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n", |
| __func__, ret0, ret1, ret2, ret3, ret4, ret5); |
| |
| return; |
| } |
| |
| static bool sm5713_fg_init(struct sm5713_fuelgauge_data *fuelgauge, bool is_surge) |
| { |
| int error_remain, ret; |
| |
| fuelgauge->info.is_FG_initialised = 0; |
| |
| if (sm5713_get_device_id(fuelgauge) < 0) { |
| return false; |
| } |
| sm5713_fg_check_battery_present(fuelgauge); |
| |
| if (fuelgauge->pdata->jig_gpio) { |
| int ret; |
| /* if (fuelgauge->pdata->jig_low_active) { */ |
| if (0) { |
| ret = request_threaded_irq(fuelgauge->pdata->jig_irq, |
| NULL, sm5713_jig_irq_thread, |
| IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
| "jig-irq", fuelgauge); |
| } else { |
| ret = request_threaded_irq(fuelgauge->pdata->jig_irq, |
| NULL, sm5713_jig_irq_thread, |
| IRQF_TRIGGER_RISING | IRQF_ONESHOT, |
| "jig-irq", fuelgauge); |
| } |
| if (ret) { |
| pr_info("%s: Failed to Request IRQ\n", |
| __func__); |
| } |
| pr_info("%s: jig_result : %d\n", __func__, sm5713_check_jig_status(fuelgauge)); |
| |
| /* initial check for the JIG */ |
| if (sm5713_check_jig_status(fuelgauge)) |
| sm5713_fg_reset_capacity_by_jig_connection(fuelgauge); |
| } |
| |
| #ifdef ENABLE_BATT_LONG_LIFE |
| fuelgauge->info.q_max_now = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_BAT_CAP); |
| pr_info("%s: q_max_now = 0x%x\n", __func__, fuelgauge->info.q_max_now); |
| #endif |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_USER_RESERV_1); |
| error_remain = (ret & I2C_ERROR_REMAIN) ? 1 : 0; |
| pr_info("%s: reserv_1 = 0x%x\n", __func__, ret); |
| |
| if (sm5713_fg_check_reg_init_need(fuelgauge) || error_remain) { |
| if (sm5713_fg_reg_init(fuelgauge, is_surge)) |
| pr_info("%s: boot time kernel init DONE!\n", __func__); |
| else |
| pr_info("%s: ERROR!! boot time kernel init ERROR!!\n", __func__); |
| } else { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_MIX_RATE, fuelgauge->info.mix_value[0]); |
| } |
| |
| sm5713_dp_setup(fuelgauge); |
| sm5713_alg_setup(fuelgauge); |
| |
| sm5713_coeff_setup(fuelgauge); |
| |
| /* for debug */ |
| sm5713_fg_buffer_read(fuelgauge); |
| |
| #ifdef ENABLE_SM5713_MQ_FUNCTION |
| /* for start mq */ |
| if (is_surge) { |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_Q_MEAS_INIT, ((fuelgauge->info.full_mq_dump<<11)/1000)); |
| } |
| sm5713_meas_mq_start(fuelgauge); |
| #endif |
| |
| sm5713_set_tcal_ioff(fuelgauge); |
| sm5713_asoc_init(fuelgauge); |
| |
| fuelgauge->info.is_FG_initialised = 1; |
| |
| return true; |
| } |
| |
| bool sm5713_fg_fuelalert_init(struct sm5713_fuelgauge_data *fuelgauge, |
| int soc) |
| { |
| /* 1. Set sm5713 alert configuration. */ |
| if (sm5713_fg_alert_init(fuelgauge, soc) > 0) |
| return true; |
| else |
| return false; |
| } |
| |
| void sm5713_fg_fuelalert_set(struct sm5713_fuelgauge_data *fuelgauge, |
| int enable) |
| { |
| u16 ret; |
| |
| ret = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_STATUS); |
| pr_info("%s: SM5713_FG_REG_STATUS(0x%x)\n", |
| __func__, ret); |
| |
| /* not use SOC alarm |
| if (ret & fuelgauge->info.irq_ctrl & ENABLE_SOC_ALARM) { |
| fuelgauge->info.soc_alert_flag = true; |
| */ /* todo more action */ |
| /* } |
| */ |
| |
| if (ret & ENABLE_V_ALARM && !lpcharge && !fuelgauge->is_charging) { |
| pr_info("%s : Battery Voltage is Very Low!! SW V EMPTY ENABLE\n", __func__); |
| |
| if (fuelgauge->vempty_mode == VEMPTY_MODE_SW || |
| fuelgauge->vempty_mode == VEMPTY_MODE_SW_VALERT) { |
| fuelgauge->vempty_mode = VEMPTY_MODE_SW_VALERT; |
| } |
| #if defined(CONFIG_BATTERY_CISD) |
| else { |
| union power_supply_propval value; |
| value.intval = fuelgauge->vempty_mode; |
| psy_do_property("battery", set, |
| POWER_SUPPLY_PROP_VOLTAGE_MIN, value); |
| } |
| #endif |
| } |
| } |
| |
| bool sm5713_fg_fuelalert_process(void *irq_data) |
| { |
| struct sm5713_fuelgauge_data *fuelgauge = |
| (struct sm5713_fuelgauge_data *)irq_data; |
| |
| sm5713_fg_fuelalert_set(fuelgauge, 0); |
| |
| return true; |
| } |
| |
| bool sm5713_fg_reset(struct sm5713_fuelgauge_data *fuelgauge, bool is_quickstart) |
| { |
| if (fuelgauge->info.is_FG_initialised == 0) { |
| pr_info("%s: Not work reset! prev init working! return! \n", __func__); |
| return true; |
| } |
| |
| pr_info("%s: Start fg reset\n", __func__); |
| /* SW reset code */ |
| fuelgauge->info.is_FG_initialised = 0; |
| sm5713_fg_verified_write_word(fuelgauge->i2c, SM5713_FG_REG_RESET, SW_RESET_CODE); |
| /* delay 1000ms */ |
| msleep(1000); |
| #ifdef ENABLE_SM5713_MQ_FUNCTION |
| sm5713_meas_mq_off(fuelgauge); |
| #endif |
| |
| if (is_quickstart) { |
| if (sm5713_fg_init(fuelgauge, false)) { |
| pr_info("%s: Quick Start - mq STOP!!\n", __func__); |
| #ifdef ENABLE_SM5713_MQ_FUNCTION |
| sm5713_meas_mq_off(fuelgauge); |
| #endif |
| } else { |
| pr_info("%s: sm5713_fg_init ERROR!!!!\n", __func__); |
| return false; |
| } |
| } |
| #ifdef ENABLE_BATT_LONG_LIFE |
| else { |
| if (sm5713_fg_init(fuelgauge, true)) { |
| pr_info("%s: BATT_LONG_LIFE reset - mq CONTINUE!!\n", __func__); |
| } else { |
| pr_info("%s: sm5713_fg_init ERROR!!!!\n", __func__); |
| return false; |
| } |
| } |
| #endif |
| |
| pr_info("%s: End fg reset\n", __func__); |
| |
| return true; |
| } |
| |
| static int sm5713_fg_check_capacity_max( |
| struct sm5713_fuelgauge_data *fuelgauge, int capacity_max) |
| { |
| int cap_max, cap_min; |
| |
| cap_max = fuelgauge->pdata->capacity_max; |
| cap_min = (fuelgauge->pdata->capacity_max - |
| fuelgauge->pdata->capacity_max_margin); |
| |
| return (capacity_max < cap_min) ? cap_min : |
| ((capacity_max >= cap_max) ? cap_max : capacity_max); |
| } |
| |
| static void sm5713_fg_get_scaled_capacity( |
| struct sm5713_fuelgauge_data *fuelgauge, |
| union power_supply_propval *val) |
| { |
| val->intval = (val->intval < fuelgauge->pdata->capacity_min) ? |
| 0 : ((val->intval - fuelgauge->pdata->capacity_min) * 1000 / |
| (fuelgauge->capacity_max - fuelgauge->pdata->capacity_min)); |
| |
| pr_info("%s: scaled capacity (%d.%d)\n", |
| __func__, val->intval/10, val->intval%10); |
| } |
| |
| |
| /* capacity is integer */ |
| static void sm5713_fg_get_atomic_capacity( |
| struct sm5713_fuelgauge_data *fuelgauge, |
| union power_supply_propval *val) |
| { |
| |
| pr_debug("%s : NOW(%d), OLD(%d)\n", |
| __func__, val->intval, fuelgauge->capacity_old); |
| |
| if (fuelgauge->pdata->capacity_calculation_type & |
| SEC_FUELGAUGE_CAPACITY_TYPE_ATOMIC) { |
| if (fuelgauge->capacity_old < val->intval) |
| val->intval = fuelgauge->capacity_old + 1; |
| else if (fuelgauge->capacity_old > val->intval) |
| val->intval = fuelgauge->capacity_old - 1; |
| } |
| |
| /* keep SOC stable in abnormal status */ |
| if (fuelgauge->pdata->capacity_calculation_type & |
| SEC_FUELGAUGE_CAPACITY_TYPE_SKIP_ABNORMAL) { |
| if (!fuelgauge->is_charging && |
| fuelgauge->capacity_old < val->intval) { |
| pr_err("%s: capacity (old %d : new %d)\n", |
| __func__, fuelgauge->capacity_old, val->intval); |
| val->intval = fuelgauge->capacity_old; |
| } |
| } |
| |
| /* updated old capacity */ |
| fuelgauge->capacity_old = val->intval; |
| } |
| |
| static int sm5713_fg_calculate_dynamic_scale( |
| struct sm5713_fuelgauge_data *fuelgauge, int capacity) |
| { |
| union power_supply_propval raw_soc_val; |
| raw_soc_val.intval = sm5713_get_soc(fuelgauge); |
| |
| if (raw_soc_val.intval < |
| fuelgauge->pdata->capacity_max - |
| fuelgauge->pdata->capacity_max_margin) { |
| pr_info("%s: raw soc(%d) is very low, skip routine\n", |
| __func__, raw_soc_val.intval); |
| } else { |
| fuelgauge->capacity_max = |
| (raw_soc_val.intval * 100 / (capacity + 1)); |
| fuelgauge->capacity_old = capacity; |
| |
| fuelgauge->capacity_max = |
| sm5713_fg_check_capacity_max(fuelgauge, |
| fuelgauge->capacity_max); |
| |
| pr_info("%s: %d is used for capacity_max, capacity(%d)\n", |
| __func__, fuelgauge->capacity_max, capacity); |
| } |
| |
| return fuelgauge->capacity_max; |
| } |
| |
| #if defined(CONFIG_EN_OOPS) |
| static void sm5713_set_full_value(struct sm5713_fuelgauge_data *fuelgauge, |
| int cable_type) |
| { |
| pr_info("%s : sm5713 todo\n", |
| __func__); |
| } |
| #endif |
| |
| #define FULL_CAPACITY 850 |
| static int calc_ttf_to_full_capacity(struct sm5713_fuelgauge_data *fuelgauge, |
| union power_supply_propval *val) |
| { |
| int i; |
| int cc_time = 0, cv_time = 0; |
| |
| int soc = FULL_CAPACITY; |
| int charge_current = val->intval; |
| struct cv_slope *cv_data = fuelgauge->cv_data; |
| int design_cap = fuelgauge->ttf_capacity; |
| |
| if (!cv_data || (val->intval <= 0)) { |
| pr_info("%s: no cv_data or val: %d\n", __func__, val->intval); |
| return -1; |
| } |
| for (i = 0; i < fuelgauge->cv_data_lenth; i++) { |
| if (charge_current >= cv_data[i].fg_current) |
| break; |
| } |
| i = i >= fuelgauge->cv_data_lenth ? fuelgauge->cv_data_lenth - 1 : i; |
| if (cv_data[i].soc < soc) { |
| for (i = 0; i < fuelgauge->cv_data_lenth; i++) { |
| if (soc <= cv_data[i].soc) |
| break; |
| } |
| cv_time = |
| ((cv_data[i - 1].time - cv_data[i].time) * (cv_data[i].soc - soc) |
| / (cv_data[i].soc - cv_data[i - 1].soc)) + cv_data[i].time; |
| } else { /* CC mode || NONE */ |
| cv_time = cv_data[i].time; |
| cc_time = design_cap * (cv_data[i].soc - soc) |
| / val->intval * 3600 / 1000; |
| pr_debug("%s: cc_time: %d\n", __func__, cc_time); |
| if (cc_time < 0) |
| cc_time = 0; |
| } |
| |
| pr_debug |
| ("%s: cap: %d, soc: %4d, T: %6d, avg: %4d, cv soc: %4d, i: %4d, val: %d\n", |
| __func__, design_cap, soc, cv_time + cc_time, |
| fuelgauge->current_avg, cv_data[i].soc, i, val->intval); |
| |
| if (cv_time + cc_time >= 0) |
| return cv_time + cc_time; |
| else |
| return 0; |
| } |
| |
| static int calc_ttf(struct sm5713_fuelgauge_data *fuelgauge, union power_supply_propval *val) |
| { |
| int i; |
| int cc_time = 0, cv_time = 0; |
| |
| int soc = fuelgauge->raw_capacity; |
| int charge_current = val->intval; |
| struct cv_slope *cv_data = fuelgauge->cv_data; |
| int design_cap = fuelgauge->ttf_capacity; |
| |
| if (!cv_data || (val->intval <= 0)) { |
| pr_info("%s: no cv_data or val: %d\n", __func__, val->intval); |
| return -1; |
| } |
| for (i = 0; i < fuelgauge->cv_data_lenth; i++) { |
| if (charge_current >= cv_data[i].fg_current) |
| break; |
| } |
| i = i >= fuelgauge->cv_data_lenth ? fuelgauge->cv_data_lenth - 1 : i; |
| if (cv_data[i].soc < soc) { |
| for (i = 0; i < fuelgauge->cv_data_lenth; i++) { |
| if (soc <= cv_data[i].soc) |
| break; |
| } |
| cv_time = ((cv_data[i-1].time - cv_data[i].time) * (cv_data[i].soc - soc)\ |
| / (cv_data[i].soc - cv_data[i-1].soc)) + cv_data[i].time; |
| } else { /* CC mode || NONE */ |
| cv_time = cv_data[i].time; |
| cc_time = design_cap * (cv_data[i].soc - soc)\ |
| / val->intval * 3600 / 1000; |
| pr_debug("%s: cc_time: %d\n", __func__, cc_time); |
| if (cc_time < 0) { |
| cc_time = 0; |
| } |
| } |
| |
| pr_debug("%s: cap: %d, soc: %4d, T: %6d, avg: %4d, cv soc: %4d, i: %4d, val: %d\n", |
| __func__, design_cap, soc, cv_time + cc_time, fuelgauge->info.batt_avgcurrent, cv_data[i].soc, i, val->intval); |
| |
| if (cv_time + cc_time >= 0) |
| return cv_time + cc_time + 60; |
| else |
| return 60; /* minimum 1minutes */ |
| } |
| |
| static void sm5713_fg_set_vempty(struct sm5713_fuelgauge_data *fuelgauge, int vempty_mode) |
| { |
| u16 data = 0; |
| u32 value_v_alarm = 0; |
| |
| if (!fuelgauge->using_temp_compensation) { |
| pr_info("%s: does not use temp compensation, default hw vempty\n", __func__); |
| vempty_mode = VEMPTY_MODE_HW; |
| } |
| |
| fuelgauge->vempty_mode = vempty_mode; |
| |
| switch (vempty_mode) { |
| case VEMPTY_MODE_SW: |
| /* HW Vempty Disable */ |
| |
| /* set volt alert threshold */ |
| value_v_alarm = (fuelgauge->battery_data->sw_v_empty_vol << 8)/1000; |
| |
| if (sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_V_L_ALARM, value_v_alarm) < 0) { |
| pr_info("%s: Failed to write VALRT_THRESHOLD_REG\n", __func__); |
| return; |
| } |
| data = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_V_L_ALARM); |
| pr_info("%s: HW V EMPTY Disable, SW V EMPTY Enable with %d mV (%d) \n", |
| __func__, fuelgauge->battery_data->sw_v_empty_vol, data); |
| break; |
| default: |
| /* HW Vempty works together with CISD v_alarm */ |
| value_v_alarm = (fuelgauge->battery_data->sw_v_empty_vol_cisd << 8)/1000; |
| |
| if (sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_V_L_ALARM, value_v_alarm) < 0) { |
| pr_info("%s: Failed to write VALRT_THRESHOLD_REG\n", __func__); |
| return; |
| } |
| data = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_V_L_ALARM); |
| pr_info("%s: HW V EMPTY Enable, SW V EMPTY Disable %d mV (%d) \n", |
| __func__, fuelgauge->battery_data->sw_v_empty_vol_cisd, data); |
| break; |
| } |
| |
| /* for v_alarm Hysteresis */ |
| value_v_alarm = ((fuelgauge->info.value_v_alarm_hys << 4) / 1000)<<4; |
| if (sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_V_ALARM_HYS, value_v_alarm) < 0) { |
| pr_info("%s: Failed to write VALRT_THRESHOLD_REG\n", __func__); |
| return; |
| } |
| data = sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_V_ALARM_HYS); |
| pr_info("%s: VALRT_THRESHOLD hysteresis set %d mV (0x%x) \n", |
| __func__, fuelgauge->info.value_v_alarm_hys, data); |
| } |
| |
| static int sm5713_fg_get_property(struct power_supply *psy, |
| enum power_supply_property psp, |
| union power_supply_propval *val) |
| { |
| struct sm5713_fuelgauge_data *fuelgauge = power_supply_get_drvdata(psy); |
| enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp; |
| /* |
| static int abnormal_current_cnt = 0; |
| */ |
| union power_supply_propval value; |
| |
| pr_info("%s: psp = 0x%x\n", __func__, psp); |
| switch (psp) { |
| /* Cell voltage (VCELL, mV) */ |
| case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
| sm5713_get_vbat(fuelgauge); |
| val->intval = fuelgauge->info.batt_voltage; |
| break; |
| case POWER_SUPPLY_PROP_VOLTAGE_AVG: |
| switch (val->intval) { |
| case SEC_BATTERY_VOLTAGE_OCV: |
| val->intval = fuelgauge->info.batt_ocv; |
| break; |
| case SEC_BATTERY_VOLTAGE_AVERAGE: |
| val->intval = fuelgauge->info.batt_avgvoltage; |
| break; |
| } |
| break; |
| /* Current */ |
| case POWER_SUPPLY_PROP_CURRENT_NOW: |
| sm5713_get_curr(fuelgauge); |
| if (val->intval == SEC_BATTERY_CURRENT_UA) |
| val->intval = fuelgauge->info.batt_current * 1000; |
| else |
| val->intval = fuelgauge->info.batt_current; |
| break; |
| /* Average Current */ |
| case POWER_SUPPLY_PROP_CURRENT_AVG: |
| if (val->intval == SEC_BATTERY_CURRENT_UA) |
| val->intval = fuelgauge->info.batt_avgcurrent * 1000; |
| else |
| val->intval = fuelgauge->info.batt_avgcurrent; |
| break; |
| /* Full Capacity */ |
| case POWER_SUPPLY_PROP_ENERGY_NOW: |
| switch (val->intval) { |
| case SEC_BATTERY_CAPACITY_DESIGNED: |
| break; |
| case SEC_BATTERY_CAPACITY_ABSOLUTE: |
| break; |
| case SEC_BATTERY_CAPACITY_TEMPERARY: |
| break; |
| case SEC_BATTERY_CAPACITY_CURRENT: |
| break; |
| case SEC_BATTERY_CAPACITY_AGEDCELL: |
| psy_do_property("battery", get, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, value); |
| val->intval = (sm5713_get_asoc(fuelgauge) * (value.intval/1000)) / 100; |
| break; |
| case SEC_BATTERY_CAPACITY_CYCLE: |
| sm5713_get_cycle(fuelgauge); |
| val->intval = fuelgauge->info.batt_soc_cycle; |
| break; |
| /* case SEC_BATTERY_CAPACITY_FULL: */ |
| #ifdef ENABLE_SM5713_MQ_FUNCTION |
| if (sm5713_meas_mq_dump(fuelgauge) > sm5713_get_full_chg_mq(fuelgauge)) |
| val->intval = sm5713_meas_mq_dump(fuelgauge); |
| else |
| val->intval = sm5713_get_full_chg_mq(fuelgauge); |
| #endif |
| /* break; */ |
| } |
| break; |
| /* SOC (%) */ |
| case POWER_SUPPLY_PROP_CAPACITY: |
| sm5713_update_all_value(fuelgauge); |
| |
| /* SM5713 F/G unit is 0.1%, raw ==> convert the unit to 0.01% */ |
| if (val->intval == SEC_FUELGAUGE_CAPACITY_TYPE_RAW) { |
| val->intval = fuelgauge->info.batt_soc * 10; |
| break; |
| } else |
| val->intval = fuelgauge->info.batt_soc; |
| |
| if (fuelgauge->pdata->capacity_calculation_type & |
| (SEC_FUELGAUGE_CAPACITY_TYPE_SCALE | |
| SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE)) { |
| sm5713_fg_get_scaled_capacity(fuelgauge, val); |
| |
| if (val->intval > 1010) { |
| pr_info("%s : scaled capacity (%d)\n", __func__, val->intval); |
| sm5713_fg_calculate_dynamic_scale(fuelgauge, 100); |
| } |
| } |
| |
| /* capacity should be between 0% and 100% |
| * (0.1% degree) |
| */ |
| if (val->intval > 1000) |
| val->intval = 1000; |
| if (val->intval < 0) |
| val->intval = 0; |
| |
| fuelgauge->raw_capacity = val->intval; |
| |
| /* get only integer part */ |
| val->intval /= 10; |
| |
| /* SW/HW V Empty setting */ |
| if (fuelgauge->using_hw_vempty) { |
| if (fuelgauge->info.temperature <= (int)fuelgauge->low_temp_limit) { |
| if (fuelgauge->raw_capacity <= 50) { |
| if (fuelgauge->vempty_mode != VEMPTY_MODE_HW) { |
| sm5713_fg_set_vempty(fuelgauge, VEMPTY_MODE_HW); |
| } |
| } else if (fuelgauge->vempty_mode == VEMPTY_MODE_HW) { |
| sm5713_fg_set_vempty(fuelgauge, VEMPTY_MODE_SW); |
| } |
| } else if (fuelgauge->vempty_mode != VEMPTY_MODE_HW) { |
| sm5713_fg_set_vempty(fuelgauge, VEMPTY_MODE_HW); |
| } |
| } |
| |
| if (!fuelgauge->is_charging && |
| fuelgauge->vempty_mode == VEMPTY_MODE_SW_VALERT && !lpcharge) { |
| pr_info("%s : SW V EMPTY. Decrease SOC\n", __func__); |
| val->intval = 0; |
| } else if ((fuelgauge->vempty_mode == VEMPTY_MODE_SW_RECOVERY) && |
| (val->intval == fuelgauge->capacity_old)) { |
| fuelgauge->vempty_mode = VEMPTY_MODE_SW; |
| } |
| |
| /* check whether doing the wake_unlock */ |
| if ((val->intval > fuelgauge->pdata->fuel_alert_soc) && |
| fuelgauge->is_fuel_alerted) { |
| sm5713_fg_fuelalert_init(fuelgauge, |
| fuelgauge->pdata->fuel_alert_soc); |
| } |
| |
| /* (Only for atomic capacity) |
| * In initial time, capacity_old is 0. |
| * and in resume from sleep, |
| * capacity_old is too different from actual soc. |
| * should update capacity_old |
| * by val->intval in booting or resume. |
| */ |
| if ((fuelgauge->initial_update_of_soc) && |
| (fuelgauge->vempty_mode != VEMPTY_MODE_SW_VALERT)) { |
| /* updated old capacity */ |
| fuelgauge->capacity_old = val->intval; |
| fuelgauge->initial_update_of_soc = false; |
| break; |
| } |
| |
| if (fuelgauge->pdata->capacity_calculation_type & |
| (SEC_FUELGAUGE_CAPACITY_TYPE_ATOMIC | |
| SEC_FUELGAUGE_CAPACITY_TYPE_SKIP_ABNORMAL)) { |
| sm5713_fg_get_atomic_capacity(fuelgauge, val); |
| } |
| break; |
| /* Battery Temperature */ |
| case POWER_SUPPLY_PROP_TEMP: |
| /* Target Temperature */ |
| case POWER_SUPPLY_PROP_TEMP_AMBIENT: |
| sm5713_get_temperature(fuelgauge); |
| val->intval = fuelgauge->info.temp_fg; |
| break; |
| #if defined(CONFIG_EN_OOPS) |
| case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: |
| return -ENODATA; |
| #endif |
| case POWER_SUPPLY_PROP_ENERGY_FULL: |
| val->intval = sm5713_get_asoc(fuelgauge); |
| break; |
| case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: |
| val->intval = fuelgauge->capacity_max; |
| break; |
| case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: |
| val->intval = calc_ttf(fuelgauge, val); |
| break; |
| case POWER_SUPPLY_PROP_CHARGE_ENABLED: |
| break; |
| case POWER_SUPPLY_PROP_CHARGE_COUNTER: |
| psy_do_property("battery", get, POWER_SUPPLY_PROP_CHARGE_FULL, value); |
| val->intval = value.intval/1000 * fuelgauge->raw_capacity; //uAh |
| break; |
| #if defined(CONFIG_BATTERY_AGE_FORECAST) |
| case POWER_SUPPLY_PROP_CAPACITY_LEVEL: |
| return -ENODATA; |
| #endif |
| case POWER_SUPPLY_PROP_MODEL_NAME: |
| val->intval = IC_TYPE_IFPMIC_SM5713; |
| break; |
| case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX: |
| switch (ext_psp) { |
| case POWER_SUPPLY_EXT_PROP_JIG_GPIO: |
| if (fuelgauge->pdata->jig_gpio) |
| val->intval = gpio_get_value(fuelgauge->pdata->jig_gpio); |
| else |
| val->intval = -1; |
| pr_info("%s: jig gpio = %d \n", __func__, val->intval); |
| break; |
| case POWER_SUPPLY_EXT_PROP_MEASURE_SYS: |
| /* not supported */ |
| val->intval = 0; |
| break; |
| case POWER_SUPPLY_EXT_PROP_VOLT_SLOPE: |
| val->intval = (sm5713_read_word(fuelgauge->i2c, SM5713_FG_REG_VOLT_CAL) & 0xFF00); |
| pr_info("%s: VOLT SLOPE = 0x%x \n", __func__, val->intval); |
| break; |
| case POWER_SUPPLY_EXT_PROP_MONITOR_WORK: |
| #if !defined(CONFIG_SEC_FACTORY) |
| sm5713_fg_periodic_read(fuelgauge); |
| #endif |
| break; |
| case POWER_SUPPLY_EXT_PROP_TTF_FULL_CAPACITY: |
| val->intval = calc_ttf_to_full_capacity(fuelgauge, val); |
| break; |
| break; |
| default: |
| return -EINVAL; |
| } |
| break; |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| #if defined(CONFIG_UPDATE_BATTERY_DATA) |
| static int sm5713_fuelgauge_parse_dt(struct sm5713_fuelgauge_data *fuelgauge); |
| #endif |
| static int sm5713_fg_set_property(struct power_supply *psy, |
| enum power_supply_property psp, |
| const union power_supply_propval *val) |
| { |
| struct sm5713_fuelgauge_data *fuelgauge = power_supply_get_drvdata(psy); |
| enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp; |
| /*u8 data[2] = {0, 0}; */ |
| |
| switch (psp) { |
| case POWER_SUPPLY_PROP_STATUS: |
| #ifdef ENABLE_BATT_LONG_LIFE |
| if (val->intval == POWER_SUPPLY_STATUS_FULL) { |
| pr_info("%s: POWER_SUPPLY_STATUS_FULL : q_max_now = 0x%x \n", __func__, fuelgauge->info.q_max_now); |
| if (fuelgauge->info.q_max_now != |
| fuelgauge->info.q_max_table[get_v_max_index_by_cycle(fuelgauge)]) { |
| if (!sm5713_fg_reset(fuelgauge, false)) |
| return -EINVAL; |
| } |
| } |
| #endif |
| break; |
| case POWER_SUPPLY_PROP_CHARGE_FULL: |
| if (fuelgauge->pdata->capacity_calculation_type & |
| SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE) |
| sm5713_fg_calculate_dynamic_scale(fuelgauge, val->intval); |
| |
| #ifdef ENABLE_SM5713_MQ_FUNCTION |
| if (sm5713_meas_mq_dump(fuelgauge) > sm5713_get_full_chg_mq(fuelgauge)) |
| sm5713_set_full_chg_mq(fuelgauge, sm5713_meas_mq_dump(fuelgauge)); |
| #endif |
| break; |
| #if defined(CONFIG_EN_OOPS) |
| case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: |
| sm5713_set_full_value(fuelgauge, val->intval); |
| break; |
| #endif |
| case POWER_SUPPLY_PROP_ONLINE: |
| fuelgauge->cable_type = val->intval; |
| if (is_nocharge_type(fuelgauge->cable_type)) { |
| fuelgauge->ta_exist = false; |
| fuelgauge->is_charging = false; |
| } else { |
| fuelgauge->ta_exist = true; |
| fuelgauge->is_charging = true; |
| |
| /* enable alert */ |
| if (fuelgauge->vempty_mode >= VEMPTY_MODE_SW_VALERT) { |
| sm5713_fg_set_vempty(fuelgauge, VEMPTY_MODE_HW); |
| fuelgauge->initial_update_of_soc = true; |
| sm5713_fg_fuelalert_init(fuelgauge, |
| fuelgauge->pdata->fuel_alert_soc); |
| } |
| } |
| break; |
| /* Battery Temperature */ |
| case POWER_SUPPLY_PROP_CAPACITY: |
| if (val->intval == SEC_FUELGAUGE_CAPACITY_TYPE_RESET) { |
| if (!sm5713_fg_reset(fuelgauge, true)) |
| return -EINVAL; |
| else |
| fuelgauge->initial_update_of_soc = true; |
| } |
| break; |
| case POWER_SUPPLY_PROP_TEMP: |
| fuelgauge->info.temperature = val->intval; |
| if (val->intval < 0) { |
| pr_info("%s: set the low temp reset! temp : %d\n", |
| __func__, val->intval); |
| } |
| break; |
| case POWER_SUPPLY_PROP_TEMP_AMBIENT: |
| break; |
| case POWER_SUPPLY_PROP_HEALTH: |
| fuelgauge->info.flag_charge_health = |
| (val->intval == POWER_SUPPLY_HEALTH_GOOD) ? 1 : 0; |
| pr_info("%s: charge health from charger = 0x%x\n", __func__, val->intval); |
| break; |
| case POWER_SUPPLY_PROP_ENERGY_NOW: |
| sm5713_fg_reset_capacity_by_jig_connection(fuelgauge); |
| break; |
| case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: |
| pr_info("%s: capacity_max changed, %d -> %d\n", |
| __func__, fuelgauge->capacity_max, val->intval); |
| fuelgauge->capacity_max = sm5713_fg_check_capacity_max(fuelgauge, val->intval); |
| fuelgauge->initial_update_of_soc = true; |
| break; |
| case POWER_SUPPLY_PROP_CHARGE_ENABLED: |
| case POWER_SUPPLY_PROP_CHARGING_ENABLED: |
| break; |
| #if defined(CONFIG_BATTERY_AGE_FORECAST) |
| case POWER_SUPPLY_PROP_CAPACITY_LEVEL: |
| pr_info("%s: full condition soc changed, %d -> %d\n", |
| __func__, fuelgauge->chg_full_soc, val->intval); |
| fuelgauge->chg_full_soc = val->intval; |
| break; |
| #endif |
| |
| #if defined(CONFIG_UPDATE_BATTERY_DATA) |
| case POWER_SUPPLY_PROP_POWER_DESIGN: |
| sm5713_fuelgauge_parse_dt(fuelgauge); |
| break; |
| #endif |
| case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX: |
| switch (ext_psp) { |
| case POWER_SUPPLY_EXT_PROP_FGSRC_SWITCHING: |
| sm5713_fg_set_jig_mode_real_vbat(fuelgauge, val->intval); |
| break; |
| case POWER_SUPPLY_EXT_PROP_FUELGAUGE_FACTORY: |
| if (val->intval) { |
| pr_info("%s: bypass mode is enabled\n", __func__); |
| sm5713_fg_reset_capacity_by_jig_connection(fuelgauge); |
| } |
| break; |
| default: |
| return -EINVAL; |
| } |
| break; |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static void sm5713_fg_isr_work(struct work_struct *work) |
| { |
| struct sm5713_fuelgauge_data *fuelgauge = |
| container_of(work, struct sm5713_fuelgauge_data, isr_work.work); |
| |
| /* process for fuel gauge chip */ |
| sm5713_fg_fuelalert_process(fuelgauge); |
| |
| wake_unlock(&fuelgauge->fuel_alert_wake_lock); |
| } |
| |
| static irqreturn_t sm5713_fg_irq_thread(int irq, void *irq_data) |
| { |
| struct sm5713_fuelgauge_data *fuelgauge = irq_data; |
| |
| pr_info("%s\n", __func__); |
| |
| if (fuelgauge->is_fuel_alerted) { |
| return IRQ_HANDLED; |
| } else { |
| wake_lock(&fuelgauge->fuel_alert_wake_lock); |
| fuelgauge->is_fuel_alerted = true; |
| schedule_delayed_work(&fuelgauge->isr_work, 0); |
| } |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int sm5713_fuelgauge_debugfs_show(struct seq_file *s, void *data) |
| { |
| struct sm5713_fuelgauge_data *fuelgauge = s->private; |
| int i; |
| u8 reg; |
| u8 reg_data; |
| |
| seq_printf(s, "SM5713 FUELGAUGE IC :\n"); |
| seq_printf(s, "===================\n"); |
| for (i = 0; i < 16; i++) { |
| if (i == 12) |
| continue; |
| for (reg = 0; reg < 0x10; reg++) { |
| reg_data = sm5713_read_word(fuelgauge->i2c, reg + i * 0x10); |
| seq_printf(s, "0x%02x:\t0x%04x\n", reg + i * 0x10, reg_data); |
| } |
| if (i == 4) |
| i = 10; |
| } |
| seq_printf(s, "\n"); |
| return 0; |
| } |
| |
| static int sm5713_fuelgauge_debugfs_open(struct inode *inode, struct file *file) |
| { |
| return single_open(file, sm5713_fuelgauge_debugfs_show, inode->i_private); |
| } |
| |
| static const struct file_operations sm5713_fuelgauge_debugfs_fops = { |
| .open = sm5713_fuelgauge_debugfs_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = single_release, |
| }; |
| |
| #ifdef CONFIG_OF |
| #define PROPERTY_NAME_SIZE 128 |
| |
| #define PINFO(format, args...) \ |
| printk(KERN_INFO "%s() line-%d: " format, \ |
| __func__, __LINE__, ## args) |
| |
| #if defined(ENABLE_BATT_LONG_LIFE) |
| static int temp_parse_dt(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| struct device_node *np = of_find_node_by_name(NULL, "battery"); |
| int len = 0, ret; |
| const u32 *p; |
| |
| if (np == NULL) { |
| pr_err("%s np NULL\n", __func__); |
| } else { |
| p = of_get_property(np, "battery,age_data", &len); |
| if (p) { |
| fuelgauge->pdata->num_age_step = len / sizeof(sec_age_data_t); |
| fuelgauge->pdata->age_data = kzalloc(len, GFP_KERNEL); |
| ret = of_property_read_u32_array(np, "battery,age_data", |
| (u32 *)fuelgauge->pdata->age_data, len/sizeof(u32)); |
| if (ret) { |
| pr_err("%s failed to read battery->pdata->age_data: %d\n", |
| __func__, ret); |
| kfree(fuelgauge->pdata->age_data); |
| fuelgauge->pdata->age_data = NULL; |
| fuelgauge->pdata->num_age_step = 0; |
| } |
| pr_info("%s num_age_step : %d\n", __func__, fuelgauge->pdata->num_age_step); |
| for (len = 0; len < fuelgauge->pdata->num_age_step; ++len) { |
| pr_info("[%d/%d]cycle:%d, float:%d, full_v:%d, recharge_v:%d, soc:%d\n", |
| len, fuelgauge->pdata->num_age_step-1, |
| fuelgauge->pdata->age_data[len].cycle, |
| fuelgauge->pdata->age_data[len].float_voltage, |
| fuelgauge->pdata->age_data[len].full_condition_vcell, |
| fuelgauge->pdata->age_data[len].recharge_condition_vcell, |
| fuelgauge->pdata->age_data[len].full_condition_soc); |
| } |
| } else { |
| fuelgauge->pdata->num_age_step = 0; |
| pr_err("%s there is not age_data\n", __func__); |
| } |
| } |
| return 0; |
| } |
| #endif |
| |
| static int sm5713_fuelgauge_parse_dt(struct sm5713_fuelgauge_data *fuelgauge) |
| { |
| char prop_name[PROPERTY_NAME_SIZE]; |
| int battery_id = -1; |
| int table[24]; |
| #ifdef ENABLE_BATT_LONG_LIFE |
| int v_max_table[5]; |
| int q_max_table[5]; |
| #endif |
| int rce_value[3]; |
| int rs_value[5]; |
| int mix_value[2]; |
| int battery_type[3]; |
| int v_alarm[2]; |
| int topoff_soc[3]; |
| int cycle_cfg[3]; |
| int v_offset_cancel[4]; |
| int temp_volcal[3]; |
| int temp_offset[6]; |
| int temp_cal[10]; |
| int volt_cal[2]; |
| int ext_temp_cal[10]; |
| int set_temp_poff[4]; |
| int curr_offset[7]; |
| int curr_cal[6]; |
| int arsm[4]; |
| int tcal_ioff[2]; |
| #ifdef ENABLE_FULL_OFFSET |
| int full_offset[5]; |
| #endif |
| |
| int ret; |
| int i, j; |
| const u32 *p; |
| int len; |
| |
| struct device_node *np = of_find_node_by_name(NULL, "sm5713-fuelgauge"); |
| |
| /* reset, irq gpio info */ |
| if (np == NULL) { |
| pr_err("%s np NULL\n", __func__); |
| } else { |
| ret = of_property_read_u32(np, "fuelgauge,capacity_max", |
| &fuelgauge->pdata->capacity_max); |
| if (ret < 0) |
| pr_err("%s error reading capacity_max %d\n", __func__, ret); |
| |
| ret = of_property_read_u32(np, "fuelgauge,capacity_max_margin", |
| &fuelgauge->pdata->capacity_max_margin); |
| if (ret < 0) { |
| pr_err("%s error reading capacity_max_margin %d\n", __func__, ret); |
| fuelgauge->pdata->capacity_max_margin = 300; |
| } |
| |
| ret = of_property_read_u32(np, "fuelgauge,capacity_min", |
| &fuelgauge->pdata->capacity_min); |
| if (ret < 0) |
| pr_err("%s error reading capacity_min %d\n", __func__, ret); |
| |
| pr_info("%s: capacity_max: %d, capacity_max_margin: %d, capacity_min: %d\n", |
| __func__, fuelgauge->pdata->capacity_max, |
| fuelgauge->pdata->capacity_max_margin, fuelgauge->pdata->capacity_min); |
| |
| ret = of_property_read_u32(np, "fuelgauge,capacity_calculation_type", |
| &fuelgauge->pdata->capacity_calculation_type); |
| if (ret < 0) |
| pr_err("%s error reading capacity_calculation_type %d\n", |
| __func__, ret); |
| ret = of_property_read_u32(np, "fuelgauge,fuel_alert_soc", |
| &fuelgauge->pdata->fuel_alert_soc); |
| if (ret < 0) |
| pr_err("%s error reading pdata->fuel_alert_soc %d\n", |
| __func__, ret); |
| fuelgauge->pdata->repeated_fuelalert = of_property_read_bool(np, |
| "fuelgaguge,repeated_fuelalert"); |
| |
| pr_info("%s: " |
| "calculation_type: 0x%x, fuel_alert_soc: %d,\n" |
| "repeated_fuelalert: %d\n", __func__, |
| fuelgauge->pdata->capacity_calculation_type, |
| fuelgauge->pdata->fuel_alert_soc, fuelgauge->pdata->repeated_fuelalert); |
| |
| |
| fuelgauge->using_temp_compensation = of_property_read_bool(np, |
| "fuelgauge,using_temp_compensation"); |
| if (fuelgauge->using_temp_compensation) { |
| ret = of_property_read_u32(np, "fuelgauge,low_temp_limit", |
| &fuelgauge->low_temp_limit); |
| if (ret < 0) |
| pr_err("%s error reading low temp limit %d\n", __func__, ret); |
| |
| pr_info("%s : LOW TEMP LIMIT(%d)\n", |
| __func__, fuelgauge->low_temp_limit); |
| } |
| |
| fuelgauge->using_hw_vempty = of_property_read_bool(np, |
| "fuelgauge,using_hw_vempty"); |
| if (fuelgauge->using_hw_vempty) { |
| ret = of_property_read_u32(np, "fuelgauge,v_empty", |
| &fuelgauge->battery_data->V_empty); |
| if (ret < 0) |
| pr_err("%s error reading v_empty %d\n", |
| __func__, ret); |
| |
| ret = of_property_read_u32(np, "fuelgauge,v_empty_origin", |
| &fuelgauge->battery_data->V_empty_origin); |
| if (ret < 0) |
| pr_err("%s error reading v_empty_origin %d\n", |
| __func__, ret); |
| |
| ret = of_property_read_u32(np, "fuelgauge,sw_v_empty_voltage_cisd", |
| &fuelgauge->battery_data->sw_v_empty_vol_cisd); |
| if (ret < 0) { |
| pr_err("%s error reading sw_v_empty_default_vol_cise %d\n", |
| __func__, ret); |
| fuelgauge->battery_data->sw_v_empty_vol_cisd = 3100; |
| } |
| |
| ret = of_property_read_u32(np, "fuelgauge,sw_v_empty_voltage", |
| &fuelgauge->battery_data->sw_v_empty_vol); |
| if (ret < 0) { |
| pr_err("%s error reading sw_v_empty_default_vol %d\n", |
| __func__, ret); |
| fuelgauge->battery_data->sw_v_empty_vol = 3200; |
| } |
| |
| ret = of_property_read_u32(np, "fuelgauge,sw_v_empty_recover_voltage", |
| &fuelgauge->battery_data->sw_v_empty_recover_vol); |
| if (ret < 0) { |
| pr_err("%s error reading sw_v_empty_recover_vol %d\n", |
| __func__, ret); |
| fuelgauge->battery_data->sw_v_empty_recover_vol = 3480; |
| } |
| |
| ret = of_property_read_u32(np, "fuelgauge,vbat_ovp", |
| &fuelgauge->battery_data->vbat_ovp); |
| if (ret < 0) { |
| pr_err("%s error reading vbat_ovp %d\n", |
| __func__, ret); |
| fuelgauge->battery_data->vbat_ovp = 4400; |
| } |
| |
| pr_info("%s : SW V Empty (%d)mV, SW V Empty recover (%d)mV, CISD V_Alarm (%d)mV, Vbat OVP (%d)mV \n", |
| __func__, |
| fuelgauge->battery_data->sw_v_empty_vol, |
| fuelgauge->battery_data->sw_v_empty_recover_vol, |
| fuelgauge->battery_data->sw_v_empty_vol_cisd, |
| fuelgauge->battery_data->vbat_ovp); |
| } |
| |
| fuelgauge->pdata->jig_gpio = of_get_named_gpio(np, "fuelgauge,jig_gpio", 0); |
| if (fuelgauge->pdata->jig_gpio < 0) { |
| pr_err("%s error reading jig_gpio = %d\n", |
| __func__, fuelgauge->pdata->jig_gpio); |
| fuelgauge->pdata->jig_gpio = 0; |
| } else { |
| fuelgauge->pdata->jig_irq = gpio_to_irq(fuelgauge->pdata->jig_gpio); |
| } |
| /* |
| if (fuelgauge->pdata->jig_gpio) { |
| ret = of_property_read_u32(np, "fuelgauge,jig_low_active", |
| &fuelgauge->pdata->jig_low_active); |
| |
| if (ret < 0) { |
| pr_err("%s error reading jig_low_active %d\n", __func__, ret); |
| fuelgauge->pdata->jig_low_active = 0; |
| } |
| } |
| */ |
| ret = of_property_read_u32(np, "fuelgauge,fg_resistor", |
| &fuelgauge->fg_resistor); |
| if (ret < 0) { |
| pr_err("%s error reading fg_resistor %d\n", |
| __func__, ret); |
| fuelgauge->fg_resistor = 1; |
| } |
| |
| |
| #if defined(CONFIG_EN_OOPS) |
| /* todo cap redesign */ |
| #endif |
| |
| ret = of_property_read_u32(np, "fuelgauge,capacity", |
| &fuelgauge->battery_data->Capacity); |
| if (ret < 0) |
| pr_err("%s error reading Capacity %d\n", |
| __func__, ret); |
| |
| ret = of_property_read_u32(np, "fuelgauge,ttf_capacity", |
| &fuelgauge->ttf_capacity); |
| if (ret < 0) |
| pr_err("%s error reading ttf_capacity %d\n", |
| __func__, ret); |
| |
| p = of_get_property(np, "fuelgauge,cv_data", &len); |
| if (p) { |
| fuelgauge->cv_data = kzalloc(len, GFP_KERNEL); |
| fuelgauge->cv_data_lenth = len / sizeof(struct cv_slope); |
| pr_err("%s len: %ld, lenth: %d, %d\n", |
| __func__, sizeof(int) * len, len, fuelgauge->cv_data_lenth); |
| ret = of_property_read_u32_array(np, "fuelgauge,cv_data", |
| (u32 *)fuelgauge->cv_data, len/sizeof(u32)); |
| |
| if (ret) { |
| pr_err("%s failed to read fuelgauge->cv_data: %d\n", |
| __func__, ret); |
| kfree(fuelgauge->cv_data); |
| fuelgauge->cv_data = NULL; |
| } |
| } else { |
| pr_err("%s there is not cv_data\n", __func__); |
| } |
| |
| |
| #if defined(CONFIG_BATTERY_AGE_FORECAST) |
| ret = of_property_read_u32(np, "battery,full_condition_soc", |
| &fuelgauge->pdata->full_condition_soc); |
| if (ret) { |
| fuelgauge->pdata->full_condition_soc = 93; |
| pr_info("%s : Full condition soc is Empty\n", __func__); |
| } |
| #endif |
| } |
| |
| /* get battery_params node for reg init */ |
| np = of_find_node_by_name(of_node_get(np), "battery_params"); |
| if (np == NULL) { |
| PINFO("Cannot find child node \"battery_params\"\n"); |
| return -EINVAL; |
| } |
| |
| /* get battery_id */ |
| if (of_property_read_u32(np, "battery,id", &battery_id) < 0) |
| PINFO("not battery,id property\n"); |
| PINFO("battery id = %d\n", battery_id); |
| |
| /* vbat measure point */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "is_read_vpack"); |
| ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.is_read_vpack, 1); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| PINFO("%s = <0x%x>\n", prop_name, fuelgauge->info.is_read_vpack); |
| |
| /* get battery_table */ |
| for (i = DISCHARGE_TABLE; i < TABLE_MAX; i++) { |
| snprintf(prop_name, PROPERTY_NAME_SIZE, |
| "battery%d,%s%d", battery_id, "battery_table", i); |
| |
| ret = of_property_read_u32_array(np, prop_name, table, 24); |
| if (ret < 0) { |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| } |
| for (j = 0; j <= FG_TABLE_LEN; j++) { |
| fuelgauge->info.battery_table[i][j] = table[j]; |
| PINFO("%s = <table[%d][%d] 0x%x>\n", prop_name, i, j, table[j]); |
| } |
| } |
| |
| snprintf(prop_name, PROPERTY_NAME_SIZE, |
| "battery%d,%s%d", battery_id, "battery_table", i); |
| |
| ret = of_property_read_u32_array(np, prop_name, table, 16); |
| if (ret < 0) { |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| } |
| for (j = 0; j <= FG_ADD_TABLE_LEN; j++) { |
| fuelgauge->info.battery_table[i][j] = table[j]; |
| PINFO("%s = <table[%d][%d] 0x%x>\n", prop_name, i, j, table[j]); |
| } |
| |
| /* get rce */ |
| for (i = 0; i < 3; i++) { |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "rce_value"); |
| ret = of_property_read_u32_array(np, prop_name, rce_value, 3); |
| if (ret < 0) { |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| } |
| fuelgauge->info.rce_value[i] = rce_value[i]; |
| } |
| PINFO("%s = <0x%x 0x%x 0x%x>\n", prop_name, rce_value[0], rce_value[1], rce_value[2]); |
| |
| /* get dtcd_value */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "dtcd_value"); |
| ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.dtcd_value, 1); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| PINFO("%s = <0x%x>\n", prop_name, fuelgauge->info.dtcd_value); |
| |
| /* get rs_value */ |
| for (i = 0; i < 5; i++) { |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "rs_value"); |
| ret = of_property_read_u32_array(np, prop_name, rs_value, 5); |
| if (ret < 0) { |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| } |
| fuelgauge->info.rs_value[i] = rs_value[i]; |
| } |
| PINFO("%s = <0x%x 0x%x 0x%x 0x%x 0x%x>\n", prop_name, rs_value[0], rs_value[1], rs_value[2], rs_value[3], rs_value[4]); |
| |
| /* get vit_period */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "vit_period"); |
| ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.vit_period, 1); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| PINFO("%s = <0x%x>\n", prop_name, fuelgauge->info.vit_period); |
| |
| /* get mix_value */ |
| for (i = 0; i < 2; i++) { |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "mix_value"); |
| ret = of_property_read_u32_array(np, prop_name, mix_value, 2); |
| if (ret < 0) { |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| } |
| fuelgauge->info.mix_value[i] = mix_value[i]; |
| } |
| PINFO("%s = <0x%x 0x%x>\n", prop_name, mix_value[0], mix_value[1]); |
| |
| /* battery_type */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "battery_type"); |
| ret = of_property_read_u32_array(np, prop_name, battery_type, 3); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.batt_v_max = battery_type[0]; |
| fuelgauge->info.min_cap = battery_type[1]; |
| fuelgauge->info.cap = battery_type[2]; |
| fuelgauge->info.maxcap = battery_type[2]; |
| if (fuelgauge->battery_data->Capacity == 0) |
| fuelgauge->battery_data->Capacity = fuelgauge->info.maxcap / 2; |
| |
| PINFO("%s = <%d %d 0x%x>\n", prop_name, |
| fuelgauge->info.batt_v_max, fuelgauge->info.min_cap, fuelgauge->info.cap); |
| |
| #ifdef ENABLE_BATT_LONG_LIFE |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "v_max_table"); |
| ret = of_property_read_u32_array(np, prop_name, v_max_table, fuelgauge->pdata->num_age_step); |
| |
| if (ret < 0) { |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| |
| for (i = 0; i < fuelgauge->pdata->num_age_step; i++) { |
| fuelgauge->info.v_max_table[i] = fuelgauge->info.battery_table[DISCHARGE_TABLE][FG_TABLE_LEN-1]; |
| PINFO("%s = <v_max_table[%d] 0x%x>\n", prop_name, i, fuelgauge->info.v_max_table[i]); |
| } |
| } else { |
| for (i = 0; i < fuelgauge->pdata->num_age_step; i++) { |
| fuelgauge->info.v_max_table[i] = v_max_table[i]; |
| PINFO("%s = <v_max_table[%d] 0x%x>\n", prop_name, i, fuelgauge->info.v_max_table[i]); |
| } |
| } |
| |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "q_max_table"); |
| ret = of_property_read_u32_array(np, prop_name, q_max_table, fuelgauge->pdata->num_age_step); |
| |
| if (ret < 0) { |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| |
| for (i = 0; i < fuelgauge->pdata->num_age_step; i++) { |
| fuelgauge->info.q_max_table[i] = fuelgauge->info.cap; |
| PINFO("%s = <q_max_table[%d] 0x%x>\n", prop_name, i, fuelgauge->info.q_max_table[i]); |
| } |
| } else { |
| for (i = 0; i < fuelgauge->pdata->num_age_step; i++) { |
| fuelgauge->info.q_max_table[i] = q_max_table[i]; |
| PINFO("%s = <q_max_table[%d] 0x%x>\n", prop_name, i, fuelgauge->info.q_max_table[i]); |
| } |
| } |
| fuelgauge->chg_full_soc = fuelgauge->pdata->age_data[0].full_condition_soc; |
| fuelgauge->info.v_max_now = fuelgauge->info.v_max_table[0]; |
| fuelgauge->info.q_max_now = fuelgauge->info.q_max_table[0]; |
| PINFO("%s = <v_max_now = 0x%x>, <q_max_now = 0x%x>, <chg_full_soc = %d>\n", prop_name, fuelgauge->info.v_max_now, fuelgauge->info.q_max_now, fuelgauge->chg_full_soc); |
| #endif |
| |
| /* MISC */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "misc"); |
| ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.misc, 1); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| PINFO("%s = <0x%x>\n", prop_name, fuelgauge->info.misc); |
| |
| /* V_ALARM */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "v_alarm"); |
| ret = of_property_read_u32_array(np, prop_name, v_alarm, 2); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.value_v_alarm = v_alarm[0]; |
| fuelgauge->info.value_v_alarm_hys = v_alarm[1]; |
| PINFO("%s = <%d %d>\n", prop_name, fuelgauge->info.value_v_alarm, fuelgauge->info.value_v_alarm_hys); |
| |
| /* TOP OFF SOC */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "topoff_soc"); |
| ret = of_property_read_u32_array(np, prop_name, topoff_soc, 3); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.enable_topoff_soc = topoff_soc[0]; |
| fuelgauge->info.topoff_soc = topoff_soc[1]; |
| fuelgauge->info.top_off = topoff_soc[2]; |
| |
| PINFO("%s = <%d %d %d>\n", prop_name, |
| fuelgauge->info.enable_topoff_soc, fuelgauge->info.topoff_soc, fuelgauge->info.top_off); |
| |
| /* SOC cycle cfg */ |
| snprintf (prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "cycle_cfg"); |
| ret = of_property_read_u32_array(np, prop_name, cycle_cfg, 3); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.cycle_high_limit = cycle_cfg[0]; |
| fuelgauge->info.cycle_low_limit = cycle_cfg[1]; |
| fuelgauge->info.cycle_limit_cntl = cycle_cfg[2]; |
| |
| PINFO("%s = <%d %d %d>\n", prop_name, |
| fuelgauge->info.cycle_high_limit, fuelgauge->info.cycle_low_limit, fuelgauge->info.cycle_limit_cntl); |
| |
| /* v_offset_cancel */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "v_offset_cancel"); |
| ret = of_property_read_u32_array(np, prop_name, v_offset_cancel, 4); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.enable_v_offset_cancel_p = v_offset_cancel[0]; |
| fuelgauge->info.enable_v_offset_cancel_n = v_offset_cancel[1]; |
| fuelgauge->info.v_offset_cancel_level = v_offset_cancel[2]; |
| fuelgauge->info.v_offset_cancel_mohm = v_offset_cancel[3]; |
| |
| PINFO("%s = <%d %d %d %d>\n", prop_name, |
| fuelgauge->info.enable_v_offset_cancel_p, fuelgauge->info.enable_v_offset_cancel_n, fuelgauge->info.v_offset_cancel_level, fuelgauge->info.v_offset_cancel_mohm); |
| |
| /* VOL & CURR CAL */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "volt_cal"); |
| ret = of_property_read_u32_array(np, prop_name, volt_cal, 2); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.volt_cal[0] = volt_cal[0]; |
| fuelgauge->info.volt_cal[1] = volt_cal[1]; |
| PINFO("%s = <0x%x 0x%x>\n", prop_name, fuelgauge->info.volt_cal[0], fuelgauge->info.volt_cal[1]); |
| |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "curr_offset"); |
| ret = of_property_read_u32_array(np, prop_name, curr_offset, 7); |
| if (ret < 0) { |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| } else { |
| fuelgauge->info.en_auto_i_offset = curr_offset[0]; |
| fuelgauge->info.ecv_i_off = curr_offset[1]; |
| fuelgauge->info.csp_i_off = curr_offset[2]; |
| fuelgauge->info.csn_i_off = curr_offset[3]; |
| fuelgauge->info.dp_ecv_i_off = curr_offset[4]; |
| fuelgauge->info.dp_csp_i_off = curr_offset[5]; |
| fuelgauge->info.dp_csn_i_off = curr_offset[6]; |
| } |
| PINFO("%s = <%d arg : 0x%x 0x%x 0x%x, dp : 0x%x 0x%x 0x%x>\n", prop_name, |
| fuelgauge->info.en_auto_i_offset, fuelgauge->info.ecv_i_off, fuelgauge->info.csp_i_off, fuelgauge->info.csn_i_off, |
| fuelgauge->info.dp_ecv_i_off, fuelgauge->info.dp_csp_i_off, fuelgauge->info.dp_csn_i_off); |
| |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "curr_cal"); |
| ret = of_property_read_u32_array(np, prop_name, curr_cal, 6); |
| if (ret < 0) { |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| } else { |
| fuelgauge->info.ecv_i_slo = curr_cal[0]; |
| fuelgauge->info.csp_i_slo = curr_cal[1]; |
| fuelgauge->info.csn_i_slo = curr_cal[2]; |
| fuelgauge->info.dp_ecv_i_slo = curr_cal[3]; |
| fuelgauge->info.dp_csp_i_slo = curr_cal[4]; |
| fuelgauge->info.dp_csn_i_slo = curr_cal[5]; |
| } |
| PINFO("%s = <arg : 0x%x 0x%x 0x%x, dp : 0x%x 0x%x 0x%x>\n", prop_name, |
| fuelgauge->info.ecv_i_slo, fuelgauge->info.csp_i_slo, fuelgauge->info.csn_i_slo, |
| fuelgauge->info.dp_ecv_i_slo, fuelgauge->info.dp_csp_i_slo, fuelgauge->info.dp_csn_i_slo); |
| |
| #ifdef ENABLE_FULL_OFFSET |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "full_offset"); |
| ret = of_property_read_u32_array(np, prop_name, full_offset, 5); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.full_offset_enable = full_offset[0]; |
| fuelgauge->info.full_offset_margin = full_offset[1]; |
| fuelgauge->info.full_extra_offset = full_offset[2]; |
| fuelgauge->info.aux_stat_base = full_offset[3]; |
| fuelgauge->info.aux_stat_check = full_offset[4]; |
| |
| PINFO("%s = <%d %d %d 0x%x 0x%x>\n", prop_name, |
| fuelgauge->info.full_offset_enable, |
| fuelgauge->info.full_offset_margin, fuelgauge->info.full_extra_offset, |
| fuelgauge->info.aux_stat_base, fuelgauge->info.aux_stat_check); |
| #endif |
| |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "coeff"); |
| ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.coeff, 1); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| PINFO("%s = <%x>\n", prop_name, fuelgauge->info.coeff); |
| |
| /* temp_std */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "temp_std"); |
| ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.temp_std, 1); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| PINFO("%s = <%d>\n", prop_name, fuelgauge->info.temp_std); |
| |
| /* temp_volcal */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "temp_volcal"); |
| ret = of_property_read_u32_array(np, prop_name, temp_volcal, 3); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.en_fg_temp_volcal = temp_volcal[0]; |
| fuelgauge->info.fg_temp_volcal_denom = temp_volcal[1]; |
| fuelgauge->info.fg_temp_volcal_fact = temp_volcal[2]; |
| PINFO("%s = <%d, %d, %d>\n", prop_name, |
| fuelgauge->info.en_fg_temp_volcal, fuelgauge->info.fg_temp_volcal_denom, fuelgauge->info.fg_temp_volcal_fact); |
| |
| /* temp_offset */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "temp_offset"); |
| ret = of_property_read_u32_array(np, prop_name, temp_offset, 6); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.en_high_fg_temp_offset = temp_offset[0]; |
| fuelgauge->info.high_fg_temp_offset_denom = temp_offset[1]; |
| fuelgauge->info.high_fg_temp_offset_fact = temp_offset[2]; |
| fuelgauge->info.en_low_fg_temp_offset = temp_offset[3]; |
| fuelgauge->info.low_fg_temp_offset_denom = temp_offset[4]; |
| fuelgauge->info.low_fg_temp_offset_fact = temp_offset[5]; |
| PINFO("%s = <%d, %d, %d, %d, %d, %d>\n", prop_name, |
| fuelgauge->info.en_high_fg_temp_offset, |
| fuelgauge->info.high_fg_temp_offset_denom, fuelgauge->info.high_fg_temp_offset_fact, |
| fuelgauge->info.en_low_fg_temp_offset, |
| fuelgauge->info.low_fg_temp_offset_denom, fuelgauge->info.low_fg_temp_offset_fact); |
| |
| /* temp_calc */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "temp_cal"); |
| ret = of_property_read_u32_array(np, prop_name, temp_cal, 10); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.en_high_fg_temp_cal = temp_cal[0]; |
| fuelgauge->info.high_fg_temp_p_cal_denom = temp_cal[1]; |
| fuelgauge->info.high_fg_temp_p_cal_fact = temp_cal[2]; |
| fuelgauge->info.high_fg_temp_n_cal_denom = temp_cal[3]; |
| fuelgauge->info.high_fg_temp_n_cal_fact = temp_cal[4]; |
| fuelgauge->info.en_low_fg_temp_cal = temp_cal[5]; |
| fuelgauge->info.low_fg_temp_p_cal_denom = temp_cal[6]; |
| fuelgauge->info.low_fg_temp_p_cal_fact = temp_cal[7]; |
| fuelgauge->info.low_fg_temp_n_cal_denom = temp_cal[8]; |
| fuelgauge->info.low_fg_temp_n_cal_fact = temp_cal[9]; |
| PINFO("%s = <%d, %d, %d, %d, %d, %d, %d, %d, %d, %d>\n", prop_name, |
| fuelgauge->info.en_high_fg_temp_cal, |
| fuelgauge->info.high_fg_temp_p_cal_denom, fuelgauge->info.high_fg_temp_p_cal_fact, |
| fuelgauge->info.high_fg_temp_n_cal_denom, fuelgauge->info.high_fg_temp_n_cal_fact, |
| fuelgauge->info.en_low_fg_temp_cal, |
| fuelgauge->info.low_fg_temp_p_cal_denom, fuelgauge->info.low_fg_temp_p_cal_fact, |
| fuelgauge->info.low_fg_temp_n_cal_denom, fuelgauge->info.low_fg_temp_n_cal_fact); |
| |
| /* ext_temp_calc */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "ext_temp_cal"); |
| ret = of_property_read_u32_array(np, prop_name, ext_temp_cal, 10); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.en_high_temp_cal = ext_temp_cal[0]; |
| fuelgauge->info.high_temp_p_cal_denom = ext_temp_cal[1]; |
| fuelgauge->info.high_temp_p_cal_fact = ext_temp_cal[2]; |
| fuelgauge->info.high_temp_n_cal_denom = ext_temp_cal[3]; |
| fuelgauge->info.high_temp_n_cal_fact = ext_temp_cal[4]; |
| fuelgauge->info.en_low_temp_cal = ext_temp_cal[5]; |
| fuelgauge->info.low_temp_p_cal_denom = ext_temp_cal[6]; |
| fuelgauge->info.low_temp_p_cal_fact = ext_temp_cal[7]; |
| fuelgauge->info.low_temp_n_cal_denom = ext_temp_cal[8]; |
| fuelgauge->info.low_temp_n_cal_fact = ext_temp_cal[9]; |
| PINFO("%s = <%d, %d, %d, %d, %d, %d, %d, %d, %d, %d>\n", prop_name, |
| fuelgauge->info.en_high_temp_cal, |
| fuelgauge->info.high_temp_p_cal_denom, fuelgauge->info.high_temp_p_cal_fact, |
| fuelgauge->info.high_temp_n_cal_denom, fuelgauge->info.high_temp_n_cal_fact, |
| fuelgauge->info.en_low_temp_cal, |
| fuelgauge->info.low_temp_p_cal_denom, fuelgauge->info.low_temp_p_cal_fact, |
| fuelgauge->info.low_temp_n_cal_denom, fuelgauge->info.low_temp_n_cal_fact); |
| |
| /* tem poff level */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "tem_poff"); |
| ret = of_property_read_u32_array(np, prop_name, set_temp_poff, 4); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.n_tem_poff = set_temp_poff[0]; |
| fuelgauge->info.n_tem_poff_offset = set_temp_poff[1]; |
| fuelgauge->info.l_tem_poff = set_temp_poff[2]; |
| fuelgauge->info.l_tem_poff_offset = set_temp_poff[3]; |
| |
| PINFO("%s = <%d, %d, %d, %d>\n", |
| prop_name, |
| fuelgauge->info.n_tem_poff, fuelgauge->info.n_tem_poff_offset, |
| fuelgauge->info.l_tem_poff, fuelgauge->info.l_tem_poff_offset); |
| |
| /* arsm setting */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "arsm"); |
| ret = of_property_read_u32_array(np, prop_name, arsm, 4); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.arsm[0] = arsm[0]; |
| fuelgauge->info.arsm[1] = arsm[1]; |
| fuelgauge->info.arsm[2] = arsm[2]; |
| fuelgauge->info.arsm[3] = arsm[3]; |
| |
| PINFO("%s = <%d, %d, %d, %d>\n", |
| prop_name, |
| fuelgauge->info.arsm[0], fuelgauge->info.arsm[1], |
| fuelgauge->info.arsm[2], fuelgauge->info.arsm[3]); |
| |
| /* age cntl value */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "age_cntl"); |
| ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.age_cntl, 1); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| PINFO("%s = <0x%x>\n", prop_name, fuelgauge->info.age_cntl); |
| |
| /* tcal_ioff setting */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "tcal_ioff"); |
| ret = of_property_read_u32_array(np, prop_name, tcal_ioff, 2); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| fuelgauge->info.tcal_ioff[0] = tcal_ioff[0]; |
| fuelgauge->info.tcal_ioff[1] = tcal_ioff[1]; |
| |
| PINFO("%s = <0x%x, 0x%x>\n", |
| prop_name, |
| fuelgauge->info.tcal_ioff[0], fuelgauge->info.tcal_ioff[1]); |
| |
| /* batt data version */ |
| snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "data_ver"); |
| ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.data_ver, 1); |
| if (ret < 0) |
| PINFO("Can get prop %s (%d)\n", prop_name, ret); |
| PINFO("%s = <%d>\n", prop_name, fuelgauge->info.data_ver); |
| |
| |
| |
| |
| return 0; |
| } |
| #endif |
| |
| ssize_t sm5713_show_attrs(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| |
| ssize_t sm5713_store_attrs(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count); |
| #define sm5713_ATTR(_name) \ |
| { \ |
| .attr = {.name = #_name, .mode = 0664}, \ |
| .show = sm5713_show_attrs, \ |
| .store = sm5713_store_attrs, \ |
| } |
| enum { |
| CHIP_ID = 0, |
| DATA, |
| DATA_1 |
| }; |
| static struct device_attribute sm5713_attrs[] = { |
| sm5713_ATTR(chip_id), |
| sm5713_ATTR(data), |
| sm5713_ATTR(data_1), |
| }; |
| static int sm5713_create_attrs(struct device *dev) |
| { |
| int i, rc; |
| |
| for (i = 0; i < (int)ARRAY_SIZE(sm5713_attrs); i++) { |
| rc = device_create_file(dev, &sm5713_attrs[i]); |
| if (rc) |
| goto create_attrs_failed; |
| } |
| return rc; |
| |
| create_attrs_failed: |
| dev_err(dev, "%s: failed (%d)\n", __func__, rc); |
| while (i--) |
| device_remove_file(dev, &sm5713_attrs[i]); |
| return rc; |
| } |
| |
| ssize_t sm5713_show_attrs(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct power_supply *psy = dev_get_drvdata(dev); |
| struct sm5713_fuelgauge_data *fuelgauge = power_supply_get_drvdata(psy); |
| const ptrdiff_t offset = attr - sm5713_attrs; |
| int i = 0; |
| u16 data; |
| |
| switch (offset) { |
| case CHIP_ID: |
| case DATA: |
| case DATA_1: |
| data = sm5713_read_word(fuelgauge->i2c, fuelgauge->read_reg); |
| i += scnprintf(buf + i, PAGE_SIZE - i, |
| "0x%02x : 0x%04x\n", fuelgauge->read_reg, data); |
| break; |
| default: |
| return -EINVAL; |
| } |
| return i; |
| } |
| |
| ssize_t sm5713_store_attrs(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct power_supply *psy = dev_get_drvdata(dev); |
| struct sm5713_fuelgauge_data *fuelgauge = power_supply_get_drvdata(psy); |
| const ptrdiff_t offset = attr - sm5713_attrs; |
| int ret = 0; |
| int x, y; |
| |
| switch (offset) { |
| case CHIP_ID: |
| ret = count; |
| break; |
| case DATA: |
| if (sscanf(buf, "0x%8x 0x%8x", &x, &y) == 2) { |
| if (x >= 0x00 && x <= 0xFF) { |
| u8 addr = x; |
| u16 data = y; |
| |
| if (sm5713_write_word(fuelgauge->i2c, addr, data) < 0) { |
| dev_info(fuelgauge->dev, |
| "%s: addr: 0x%x write fail\n", __func__, addr); |
| } |
| } else { |
| dev_info(fuelgauge->dev, |
| "%s: addr: 0x%x is wrong\n", __func__, x); |
| } |
| } |
| ret = count; |
| break; |
| case DATA_1: |
| if (sscanf(buf, "0x%8x", &x) == 1) { |
| fuelgauge->read_reg = x; |
| } |
| ret = count; |
| break; |
| default: |
| ret = -EINVAL; |
| } |
| return ret; |
| } |
| |
| static const struct power_supply_desc sm5713_fuelgauge_power_supply_desc = { |
| .name = "sm5713-fuelgauge", |
| .type = POWER_SUPPLY_TYPE_UNKNOWN, |
| .properties = sm5713_fuelgauge_props, |
| .num_properties = ARRAY_SIZE(sm5713_fuelgauge_props), |
| .get_property = sm5713_fg_get_property, |
| .set_property = sm5713_fg_set_property, |
| }; |
| |
| static int sm5713_fuelgauge_probe(struct platform_device *pdev) |
| { |
| struct sm5713_dev *sm5713 = dev_get_drvdata(pdev->dev.parent); |
| struct sm5713_platform_data *pdata = dev_get_platdata(sm5713->dev); |
| struct sm5713_fuelgauge_data *fuelgauge; |
| sec_fuelgauge_platform_data_t *fuelgauge_data; |
| /* struct power_supply_config fuelgauge_cfg = {}; */ |
| struct power_supply_config psy_fg = {}; |
| int ret = 0; |
| union power_supply_propval raw_soc_val; |
| |
| pr_info("%s: SM5713 Fuelgauge Driver Loading\n", __func__); |
| |
| fuelgauge = kzalloc(sizeof(*fuelgauge), GFP_KERNEL); |
| if (!fuelgauge) |
| return -ENOMEM; |
| |
| fuelgauge_data = kzalloc(sizeof(sec_fuelgauge_platform_data_t), GFP_KERNEL); |
| if (!fuelgauge_data) { |
| ret = -ENOMEM; |
| goto err_free; |
| } |
| |
| mutex_init(&fuelgauge->fg_lock); |
| |
| fuelgauge->dev = &pdev->dev; |
| fuelgauge->pdata = fuelgauge_data; |
| fuelgauge->i2c = sm5713->fuelgauge; |
| /* fuelgauge->pmic = sm5713->i2c; */ |
| fuelgauge->sm5713_pdata = pdata; |
| |
| fuelgauge->pmic_rev = sm5713->pmic_rev; |
| fuelgauge->vender_id = sm5713->vender_id; |
| |
| #if defined(ENABLE_BATT_LONG_LIFE) |
| temp_parse_dt(fuelgauge); |
| #endif |
| |
| #if defined(CONFIG_OF) |
| fuelgauge->battery_data = kzalloc(sizeof(struct battery_data_t), |
| GFP_KERNEL); |
| if (!fuelgauge->battery_data) { |
| pr_err("Failed to allocate memory\n"); |
| ret = -ENOMEM; |
| goto err_pdata_free; |
| } |
| ret = sm5713_fuelgauge_parse_dt(fuelgauge); |
| if (ret < 0) { |
| pr_err("%s not found charger dt! ret[%d]\n", |
| __func__, ret); |
| } |
| #endif |
| |
| /* initialize value */ |
| fuelgauge->isjigmoderealvbat = false; |
| |
| platform_set_drvdata(pdev, fuelgauge); |
| |
| (void) debugfs_create_file("sm5713-fuelgauge-regs", |
| S_IRUGO, NULL, (void *)fuelgauge, &sm5713_fuelgauge_debugfs_fops); |
| |
| if (!sm5713_fg_init(fuelgauge, false)) { |
| pr_err("%s: Failed to Initialize Fuelgauge\n", __func__); |
| goto err_data_free; |
| } |
| |
| fuelgauge->capacity_max = fuelgauge->pdata->capacity_max; |
| raw_soc_val.intval = sm5713_get_soc(fuelgauge); |
| |
| if (raw_soc_val.intval > fuelgauge->capacity_max) |
| sm5713_fg_calculate_dynamic_scale(fuelgauge, 100); |
| |
| /* SW/HW init code. SW/HW V Empty mode must be opposite ! */ |
| fuelgauge->info.temperature = 300; /* default value */ |
| pr_info("%s: SW/HW V empty init \n", __func__); |
| sm5713_fg_set_vempty(fuelgauge, VEMPTY_MODE_HW); |
| |
| psy_fg.drv_data = fuelgauge; |
| psy_fg.supplied_to = sm5713_fg_supplied_to; |
| psy_fg.num_supplicants = ARRAY_SIZE(sm5713_fg_supplied_to), |
| |
| fuelgauge->psy_fg = power_supply_register(&pdev->dev, &sm5713_fuelgauge_power_supply_desc, &psy_fg); |
| if (!fuelgauge->psy_fg) { |
| dev_err(&pdev->dev, "%s: failed to power supply fg register", __func__); |
| goto err_data_free; |
| } |
| /* |
| fuelgauge->psy_fg.desc->name = "sm5713-fuelgauge"; |
| fuelgauge->psy_fg.desc->type = POWER_SUPPLY_TYPE_UNKNOWN; |
| fuelgauge->psy_fg.desc->get_property = sm5713_fg_get_property; |
| fuelgauge->psy_fg.desc->set_property = sm5713_fg_set_property; |
| fuelgauge->psy_fg.desc->properties = sm5713_fuelgauge_props; |
| fuelgauge->psy_fg.desc->num_properties = ARRAY_SIZE(sm5713_fuelgauge_props); |
| |
| ret = power_supply_register(&pdev->dev, &fuelgauge->psy_fg); |
| |
| if (ret) { |
| pr_err("%s: Failed to Register psy_fg\n", __func__); |
| goto err_data_free; |
| } |
| */ |
| fuelgauge->fg_irq = pdata->irq_base + SM5713_FG_IRQ_INT_LOW_VOLTAGE; |
| pr_info("[%s]IRQ_BASE(%d) FG_IRQ(%d)\n", |
| __func__, pdata->irq_base, fuelgauge->fg_irq); |
| |
| fuelgauge->is_fuel_alerted = false; |
| if (fuelgauge->pdata->fuel_alert_soc >= 0) { |
| if (sm5713_fg_fuelalert_init(fuelgauge, |
| fuelgauge->pdata->fuel_alert_soc)) { |
| wake_lock_init(&fuelgauge->fuel_alert_wake_lock, |
| WAKE_LOCK_SUSPEND, "fuel_alerted"); |
| if (fuelgauge->fg_irq) { |
| INIT_DELAYED_WORK(&fuelgauge->isr_work, sm5713_fg_isr_work); |
| |
| ret = request_threaded_irq(fuelgauge->fg_irq, |
| NULL, sm5713_fg_irq_thread, |
| 0, |
| "fuelgauge-irq", fuelgauge); |
| if (ret) { |
| pr_err("%s: Failed to Request IRQ\n", __func__); |
| goto err_supply_unreg; |
| } |
| } |
| } else { |
| pr_err("%s: Failed to Initialize Fuel-alert\n", |
| __func__); |
| goto err_supply_unreg; |
| } |
| } |
| |
| fuelgauge->initial_update_of_soc = true; |
| ret = sm5713_create_attrs(&fuelgauge->psy_fg->dev); |
| if (ret) { |
| dev_err(sm5713->dev, |
| "%s : Failed to create_attrs\n", __func__); |
| } |
| |
| pr_info("%s: SM5713 Fuelgauge Driver Loaded\n", __func__); |
| return 0; |
| |
| err_supply_unreg: |
| power_supply_unregister(fuelgauge->psy_fg); |
| err_data_free: |
| #if defined(CONFIG_OF) |
| kfree(fuelgauge->battery_data); |
| #endif |
| err_pdata_free: |
| kfree(fuelgauge_data); |
| mutex_destroy(&fuelgauge->fg_lock); |
| err_free: |
| kfree(fuelgauge); |
| |
| return ret; |
| } |
| |
| static int sm5713_fuelgauge_remove(struct platform_device *pdev) |
| { |
| struct sm5713_fuelgauge_data *fuelgauge = |
| platform_get_drvdata(pdev); |
| |
| if (fuelgauge->pdata->fuel_alert_soc >= 0) |
| wake_lock_destroy(&fuelgauge->fuel_alert_wake_lock); |
| |
| return 0; |
| } |
| |
| static int sm5713_fuelgauge_suspend(struct device *dev) |
| { |
| return 0; |
| } |
| |
| static int sm5713_fuelgauge_resume(struct device *dev) |
| { |
| struct sm5713_fuelgauge_data *fuelgauge = dev_get_drvdata(dev); |
| |
| fuelgauge->initial_update_of_soc = true; |
| |
| return 0; |
| } |
| |
| static void sm5713_fuelgauge_shutdown(struct platform_device *pdev) |
| { |
| struct sm5713_fuelgauge_data *fuelgauge = platform_get_drvdata(pdev); |
| |
| pr_info("%s: ++\n", __func__); |
| |
| if (fuelgauge->using_hw_vempty) |
| sm5713_fg_set_vempty(fuelgauge, false); |
| #ifdef ENABLE_SM5713_MQ_FUNCTION |
| sm5713_meas_mq_suspend(fuelgauge); |
| #endif |
| sm5713_write_word(fuelgauge->i2c, SM5713_FG_REG_MIX_RATE, 0x0F03); |
| |
| pr_info("%s: --\n", __func__); |
| } |
| |
| static SIMPLE_DEV_PM_OPS(sm5713_fuelgauge_pm_ops, sm5713_fuelgauge_suspend, |
| sm5713_fuelgauge_resume); |
| |
| static struct platform_driver sm5713_fuelgauge_driver = { |
| .driver = { |
| .name = "sm5713-fuelgauge", |
| .owner = THIS_MODULE, |
| #ifdef CONFIG_PM |
| .pm = &sm5713_fuelgauge_pm_ops, |
| #endif |
| }, |
| .probe = sm5713_fuelgauge_probe, |
| .remove = sm5713_fuelgauge_remove, |
| .shutdown = sm5713_fuelgauge_shutdown, |
| }; |
| |
| static int __init sm5713_fuelgauge_init(void) |
| { |
| pr_info("%s: \n", __func__); |
| return platform_driver_register(&sm5713_fuelgauge_driver); |
| } |
| |
| static void __exit sm5713_fuelgauge_exit(void) |
| { |
| platform_driver_unregister(&sm5713_fuelgauge_driver); |
| } |
| module_init(sm5713_fuelgauge_init); |
| module_exit(sm5713_fuelgauge_exit); |
| |
| MODULE_DESCRIPTION("Samsung SM5713 Fuel Gauge Driver"); |
| MODULE_AUTHOR("Samsung Electronics"); |
| MODULE_LICENSE("GPL"); |