blob: 004363772031b11a5f7458766a14dafb8ba6f459 [file] [log] [blame]
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "FG: %s: " fmt, __func__
#include <linux/atomic.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/rtc.h>
#include <linux/err.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/spmi.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/ktime.h>
#include <linux/power_supply.h>
#include <linux/of_batterydata.h>
#include <linux/string_helpers.h>
#include <linux/alarmtimer.h>
#include <linux/qpnp-revid.h>
/* Register offsets */
/* Interrupt offsets */
#define INT_RT_STS(base) (base + 0x10)
#define INT_EN_CLR(base) (base + 0x16)
/* SPMI Register offsets */
#define SOC_MONOTONIC_SOC 0x09
#define OTP_CFG1 0xE2
#define SOC_BOOT_MOD 0x50
#define SOC_RESTART 0x51
#define REG_OFFSET_PERP_SUBTYPE 0x05
/* RAM register offsets */
#define RAM_OFFSET 0x400
/* Bit/Mask definitions */
#define FULL_PERCENT 0xFF
#define MAX_TRIES_SOC 5
#define MA_MV_BIT_RES 39
#define MSB_SIGN BIT(7)
#define IBAT_VBAT_MASK 0x7F
#define NO_OTP_PROF_RELOAD BIT(6)
#define REDO_FIRST_ESTIMATE BIT(3)
#define RESTART_GO BIT(0)
/* SUBTYPE definitions */
#define FG_SOC 0x9
#define FG_BATT 0xA
#define FG_ADC 0xB
#define FG_MEMIF 0xC
#define QPNP_FG_DEV_NAME "qcom,qpnp-fg"
#define MEM_IF_TIMEOUT_MS 5000
#define BUCKET_COUNT 8
#define BUCKET_SOC_PCT (256 / BUCKET_COUNT)
#define BCL_MA_TO_ADC(_current, _adc_val) { \
_adc_val = (u8)((_current) * 100 / 976); \
}
/* Debug Flag Definitions */
enum {
FG_SPMI_DEBUG_WRITES = BIT(0), /* Show SPMI writes */
FG_SPMI_DEBUG_READS = BIT(1), /* Show SPMI reads */
FG_IRQS = BIT(2), /* Show interrupts */
FG_MEM_DEBUG_WRITES = BIT(3), /* Show SRAM writes */
FG_MEM_DEBUG_READS = BIT(4), /* Show SRAM reads */
FG_POWER_SUPPLY = BIT(5), /* Show POWER_SUPPLY */
FG_STATUS = BIT(6), /* Show FG status changes */
FG_AGING = BIT(7), /* Show FG aging algorithm */
};
enum dig_major {
DIG_REV_8994_1 = 0x1,
DIG_REV_8994_2 = 0x2,
};
enum pmic_subtype {
PMI8994 = 10,
PMI8950 = 17,
};
struct fg_mem_setting {
u16 address;
u8 offset;
int value;
};
struct fg_mem_data {
u16 address;
u8 offset;
unsigned int len;
int value;
};
struct fg_learning_data {
int64_t cc_uah;
int64_t learned_cc_uah;
bool active;
bool feedback_on;
struct mutex learning_lock;
ktime_t time_stamp;
/* configuration properties */
int max_start_soc;
int max_increment;
int max_decrement;
int min_temp;
int max_temp;
int vbat_est_thr_uv;
};
struct fg_rslow_data {
u8 rslow_cfg;
u8 rslow_thr;
u8 rs_to_rslow[2];
u8 rslow_comp[4];
uint32_t chg_rs_to_rslow;
uint32_t chg_rslow_comp_c1;
uint32_t chg_rslow_comp_c2;
uint32_t chg_rslow_comp_thr;
bool active;
struct mutex lock;
};
struct fg_cyc_ctr_data {
bool en;
bool started[BUCKET_COUNT];
u16 count[BUCKET_COUNT];
u8 last_soc[BUCKET_COUNT];
int id;
struct mutex lock;
};
/* FG_MEMIF setting index */
enum fg_mem_setting_index {
FG_MEM_SOFT_COLD = 0,
FG_MEM_SOFT_HOT,
FG_MEM_HARD_COLD,
FG_MEM_HARD_HOT,
FG_MEM_RESUME_SOC,
FG_MEM_BCL_LM_THRESHOLD,
FG_MEM_BCL_MH_THRESHOLD,
FG_MEM_TERM_CURRENT,
FG_MEM_CHG_TERM_CURRENT,
FG_MEM_IRQ_VOLT_EMPTY,
FG_MEM_CUTOFF_VOLTAGE,
FG_MEM_VBAT_EST_DIFF,
FG_MEM_DELTA_SOC,
FG_MEM_SOC_MAX,
FG_MEM_SOC_MIN,
FG_MEM_BATT_LOW,
FG_MEM_SETTING_MAX,
};
/* FG_MEMIF data index */
enum fg_mem_data_index {
FG_DATA_BATT_TEMP = 0,
FG_DATA_OCV,
FG_DATA_VOLTAGE,
FG_DATA_CURRENT,
FG_DATA_BATT_ESR,
FG_DATA_BATT_ESR_COUNT,
FG_DATA_BATT_SOC,
FG_DATA_CC_CHARGE,
FG_DATA_VINT_ERR,
FG_DATA_CPRED_VOLTAGE,
/* values below this only gets read once per profile reload */
FG_DATA_BATT_ID,
FG_DATA_BATT_ID_INFO,
FG_DATA_MAX,
};
#define SETTING(_idx, _address, _offset, _value) \
[FG_MEM_##_idx] = { \
.address = _address, \
.offset = _offset, \
.value = _value, \
} \
/* modify the battery warm and hot temperature threshold */
/* as our battery warm/hot temperature is 45 and 60 degree */
static struct fg_mem_setting settings[FG_MEM_SETTING_MAX] = {
/* ID Address, Offset, Value*/
SETTING(SOFT_COLD, 0x454, 0, 100),
SETTING(SOFT_HOT, 0x454, 1, 450),
SETTING(HARD_COLD, 0x454, 2, 50),
SETTING(HARD_HOT, 0x454, 3, 600),
SETTING(RESUME_SOC, 0x45C, 1, 0),
SETTING(BCL_LM_THRESHOLD, 0x47C, 2, 50),
SETTING(BCL_MH_THRESHOLD, 0x47C, 3, 752),
SETTING(TERM_CURRENT, 0x40C, 2, 250),
SETTING(CHG_TERM_CURRENT, 0x4F8, 2, 250),
SETTING(IRQ_VOLT_EMPTY, 0x458, 3, 3100),
SETTING(CUTOFF_VOLTAGE, 0x40C, 0, 3200),
SETTING(VBAT_EST_DIFF, 0x000, 0, 30),
SETTING(DELTA_SOC, 0x450, 3, 1),
SETTING(SOC_MAX, 0x458, 1, 85),
SETTING(SOC_MIN, 0x458, 2, 15),
SETTING(BATT_LOW, 0x458, 0, 4200),
};
#define DATA(_idx, _address, _offset, _length, _value) \
[FG_DATA_##_idx] = { \
.address = _address, \
.offset = _offset, \
.len = _length, \
.value = _value, \
} \
static struct fg_mem_data fg_data[FG_DATA_MAX] = {
/* ID Address, Offset, Length, Value*/
DATA(BATT_TEMP, 0x550, 2, 2, -EINVAL),
DATA(OCV, 0x588, 3, 2, -EINVAL),
DATA(VOLTAGE, 0x5CC, 1, 2, -EINVAL),
DATA(CURRENT, 0x5CC, 3, 2, -EINVAL),
DATA(BATT_ESR, 0x554, 2, 2, -EINVAL),
DATA(BATT_ESR_COUNT, 0x558, 2, 2, -EINVAL),
DATA(BATT_SOC, 0x56C, 1, 3, -EINVAL),
DATA(CC_CHARGE, 0x570, 0, 4, -EINVAL),
DATA(VINT_ERR, 0x560, 0, 4, -EINVAL),
DATA(CPRED_VOLTAGE, 0x540, 0, 2, -EINVAL),
DATA(BATT_ID, 0x594, 1, 1, -EINVAL),
DATA(BATT_ID_INFO, 0x594, 3, 1, -EINVAL),
};
static int fg_debug_mask;
module_param_named(
debug_mask, fg_debug_mask, int, S_IRUSR | S_IWUSR
);
static int fg_sense_type = -EINVAL;
static int fg_restart;
static int fg_est_dump;
module_param_named(
first_est_dump, fg_est_dump, int, S_IRUSR | S_IWUSR
);
static char *fg_batt_type;
module_param_named(
battery_type, fg_batt_type, charp, S_IRUSR | S_IWUSR
);
static int fg_sram_update_period_ms = 30000;
module_param_named(
sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR
);
struct fg_irq {
int irq;
unsigned long disabled;
};
enum fg_soc_irq {
HIGH_SOC,
LOW_SOC,
FULL_SOC,
EMPTY_SOC,
DELTA_SOC,
FIRST_EST_DONE,
SW_FALLBK_OCV,
SW_FALLBK_NEW_BATTRT_STS,
FG_SOC_IRQ_COUNT,
};
enum fg_batt_irq {
JEITA_SOFT_COLD,
JEITA_SOFT_HOT,
VBATT_LOW,
BATT_IDENTIFIED,
BATT_ID_REQ,
BATTERY_UNKNOWN,
BATT_MISSING,
BATT_MATCH,
FG_BATT_IRQ_COUNT,
};
enum fg_mem_if_irq {
FG_MEM_AVAIL,
TA_RCVRY_SUG,
FG_MEM_IF_IRQ_COUNT,
};
enum fg_batt_aging_mode {
FG_AGING_NONE,
FG_AGING_ESR,
FG_AGING_CC,
};
enum register_type {
MEM_INTF_CFG,
MEM_INTF_CTL,
MEM_INTF_ADDR_LSB,
MEM_INTF_RD_DATA0,
MEM_INTF_WR_DATA0,
MAX_ADDRESS,
};
struct register_offset {
u16 address[MAX_ADDRESS];
};
static struct register_offset offset[] = {
[0] = {
/* CFG CTL LSB RD0 WD0 */
.address = {0x40, 0x41, 0x42, 0x4C, 0x48},
},
};
#define MEM_INTF_CFG(chip) \
((chip)->mem_base + (chip)->offset[MEM_INTF_CFG])
#define MEM_INTF_CTL(chip) \
((chip)->mem_base + (chip)->offset[MEM_INTF_CTL])
#define MEM_INTF_ADDR_LSB(chip) \
((chip)->mem_base + (chip)->offset[MEM_INTF_ADDR_LSB])
#define MEM_INTF_RD_DATA0(chip) \
((chip)->mem_base + (chip)->offset[MEM_INTF_RD_DATA0])
#define MEM_INTF_WR_DATA0(chip) \
((chip)->mem_base + (chip)->offset[MEM_INTF_WR_DATA0])
struct fg_wakeup_source {
struct wakeup_source source;
unsigned long enabled;
};
static void fg_stay_awake(struct fg_wakeup_source *source)
{
if (!__test_and_set_bit(0, &source->enabled)) {
__pm_stay_awake(&source->source);
pr_debug("enabled source %s\n", source->source.name);
}
}
static void fg_relax(struct fg_wakeup_source *source)
{
if (__test_and_clear_bit(0, &source->enabled)) {
__pm_relax(&source->source);
pr_debug("disabled source %s\n", source->source.name);
}
}
#define THERMAL_COEFF_N_BYTES 6
struct fg_chip {
struct device *dev;
struct spmi_device *spmi;
u8 pmic_subtype;
u8 revision[4];
u16 soc_base;
u16 batt_base;
u16 mem_base;
u16 vbat_adc_addr;
u16 ibat_adc_addr;
u16 tp_rev_addr;
atomic_t memif_user_cnt;
struct fg_irq soc_irq[FG_SOC_IRQ_COUNT];
struct fg_irq batt_irq[FG_BATT_IRQ_COUNT];
struct fg_irq mem_irq[FG_MEM_IF_IRQ_COUNT];
struct completion sram_access_granted;
struct completion sram_access_revoked;
struct completion batt_id_avail;
struct power_supply bms_psy;
struct mutex rw_lock;
struct mutex sysfs_restart_lock;
struct work_struct batt_profile_init;
struct work_struct dump_sram;
struct work_struct status_change_work;
struct work_struct cycle_count_work;
struct work_struct battery_age_work;
struct work_struct update_esr_work;
struct work_struct set_resume_soc_work;
struct work_struct rslow_comp_work;
struct work_struct sysfs_restart_work;
struct work_struct init_work;
struct work_struct charge_full_work;
struct power_supply *batt_psy;
struct power_supply *usb_psy;
struct power_supply *dc_psy;
struct fg_wakeup_source memif_wakeup_source;
struct fg_wakeup_source profile_wakeup_source;
struct fg_wakeup_source empty_check_wakeup_source;
struct fg_wakeup_source resume_soc_wakeup_source;
bool first_profile_loaded;
struct fg_wakeup_source update_temp_wakeup_source;
struct fg_wakeup_source update_sram_wakeup_source;
bool profile_loaded;
bool use_otp_profile;
bool battery_missing;
bool power_supply_registered;
bool sw_rbias_ctrl;
bool use_thermal_coefficients;
bool esr_strict_filter;
bool soc_empty;
bool charge_done;
bool resume_soc_lowered;
bool vbat_low_irq_enabled;
bool charge_full;
bool hold_soc_while_full;
struct delayed_work update_jeita_setting;
struct delayed_work update_sram_data;
struct delayed_work update_temp_work;
struct delayed_work check_empty_work;
char *batt_profile;
u8 thermal_coefficients[THERMAL_COEFF_N_BYTES];
u32 cc_cv_threshold_mv;
unsigned int batt_profile_len;
unsigned int batt_max_voltage_uv;
const char *batt_type;
const char *batt_psy_name;
unsigned long last_sram_update_time;
unsigned long last_temp_update_time;
int64_t ocv_coeffs[12];
int64_t cutoff_voltage;
int evaluation_current;
int ocv_junction_p1p2;
int ocv_junction_p2p3;
int nom_cap_uah;
int actual_cap_uah;
int status;
int prev_status;
int health;
enum fg_batt_aging_mode batt_aging_mode;
/* capacity learning */
struct fg_learning_data learning_data;
struct alarm fg_cap_learning_alarm;
struct work_struct fg_cap_learning_work;
/* rslow compensation */
struct fg_rslow_data rslow_comp;
/* cycle counter */
struct fg_cyc_ctr_data cyc_ctr;
/* interleaved memory access */
u16 *offset;
};
/* FG_MEMIF DEBUGFS structures */
#define ADDR_LEN 4 /* 3 byte address + 1 space character */
#define CHARS_PER_ITEM 3 /* Format is 'XX ' */
#define ITEMS_PER_LINE 4 /* 4 data items per line */
#define MAX_LINE_LENGTH (ADDR_LEN + (ITEMS_PER_LINE * CHARS_PER_ITEM) + 1)
#define MAX_REG_PER_TRANSACTION (8)
static const char *DFS_ROOT_NAME = "fg_memif";
static const mode_t DFS_MODE = S_IRUSR | S_IWUSR;
static const char *default_batt_type = "Unknown Battery";
static const char *loading_batt_type = "Loading Battery Data";
static const char *missing_batt_type = "Disconnected Battery";
/* Log buffer */
struct fg_log_buffer {
size_t rpos; /* Current 'read' position in buffer */
size_t wpos; /* Current 'write' position in buffer */
size_t len; /* Length of the buffer */
char data[0]; /* Log buffer */
};
/* transaction parameters */
struct fg_trans {
u32 cnt; /* Number of bytes to read */
u16 addr; /* 12-bit address in SRAM */
u32 offset; /* Offset of last read data + byte offset */
struct fg_chip *chip;
struct fg_log_buffer *log; /* log buffer */
u8 *data; /* fg data that is read */
};
struct fg_dbgfs {
u32 cnt;
u32 addr;
struct fg_chip *chip;
struct dentry *root;
struct mutex lock;
struct debugfs_blob_wrapper help_msg;
};
static struct fg_dbgfs dbgfs_data = {
.lock = __MUTEX_INITIALIZER(dbgfs_data.lock),
.help_msg = {
.data =
"FG Debug-FS support\n"
"\n"
"Hierarchy schema:\n"
"/sys/kernel/debug/fg_memif\n"
" /help -- Static help text\n"
" /address -- Starting register address for reads or writes\n"
" /count -- Number of registers to read (only used for reads)\n"
" /data -- Initiates the SRAM read (formatted output)\n"
"\n",
},
};
static const struct of_device_id fg_match_table[] = {
{ .compatible = QPNP_FG_DEV_NAME, },
{}
};
static char *fg_supplicants[] = {
"battery",
"bcl",
"fg_adc"
};
#define DEBUG_PRINT_BUFFER_SIZE 64
static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len)
{
int pos = 0;
int i;
for (i = 0; i < buf_len; i++) {
pos += scnprintf(str + pos, str_len - pos, "%02X", buf[i]);
if (i < buf_len - 1)
pos += scnprintf(str + pos, str_len - pos, " ");
}
}
static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len)
{
int rc = 0;
struct spmi_device *spmi = chip->spmi;
char str[DEBUG_PRINT_BUFFER_SIZE];
if ((addr & 0xff00) == 0) {
pr_err("addr cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
addr, spmi->sid, rc);
return -EINVAL;
}
rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, addr, val, len);
if (rc) {
pr_err("write failed addr=0x%02x sid=0x%02x rc=%d\n",
addr, spmi->sid, rc);
return rc;
}
if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_WRITES)) {
str[0] = '\0';
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
pr_info("write(0x%04X), sid=%d, len=%d; %s\n",
addr, spmi->sid, len, str);
}
return rc;
}
static int fg_read(struct fg_chip *chip, u8 *val, u16 addr, int len)
{
int rc = 0;
struct spmi_device *spmi = chip->spmi;
char str[DEBUG_PRINT_BUFFER_SIZE];
if ((addr & 0xff00) == 0) {
pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
addr, spmi->sid, rc);
return -EINVAL;
}
rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, val, len);
if (rc) {
pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", addr,
spmi->sid, rc);
return rc;
}
if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_READS)) {
str[0] = '\0';
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
pr_info("read(0x%04x), sid=%d, len=%d; %s\n",
addr, spmi->sid, len, str);
}
return rc;
}
static int fg_masked_write(struct fg_chip *chip, u16 addr,
u8 mask, u8 val, int len)
{
int rc;
u8 reg;
rc = fg_read(chip, &reg, addr, len);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc);
return rc;
}
pr_debug("addr = 0x%x read 0x%x\n", addr, reg);
reg &= ~mask;
reg |= val & mask;
pr_debug("Writing 0x%x\n", reg);
rc = fg_write(chip, &reg, addr, len);
if (rc) {
pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
return rc;
}
return rc;
}
#define RIF_MEM_ACCESS_REQ BIT(7)
static inline bool fg_check_sram_access(struct fg_chip *chip)
{
int rc;
u8 mem_if_sts;
rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
if (rc) {
pr_err("failed to read mem status rc=%d\n", rc);
return 0;
}
if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0)
return false;
rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1);
if (rc) {
pr_err("failed to read mem status rc=%d\n", rc);
return 0;
}
if ((mem_if_sts & RIF_MEM_ACCESS_REQ) == 0)
return false;
return true;
}
#define RIF_MEM_ACCESS_REQ BIT(7)
static inline int fg_assert_sram_access(struct fg_chip *chip)
{
int rc;
u8 mem_if_sts;
rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
if (rc) {
pr_err("failed to read mem status rc=%d\n", rc);
return rc;
}
if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0) {
pr_err("mem_avail not high: %02x\n", mem_if_sts);
return -EINVAL;
}
rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1);
if (rc) {
pr_err("failed to read mem status rc=%d\n", rc);
return rc;
}
if ((mem_if_sts & RIF_MEM_ACCESS_REQ) == 0) {
pr_err("mem_avail not high: %02x\n", mem_if_sts);
return -EINVAL;
}
return 0;
}
#define INTF_CTL_BURST BIT(7)
#define INTF_CTL_WR_EN BIT(6)
static int fg_config_access(struct fg_chip *chip, bool write,
bool burst, bool otp)
{
int rc;
u8 intf_ctl = 0;
if (otp) {
/* Configure OTP access */
rc = fg_masked_write(chip, chip->mem_base + OTP_CFG1,
0xFF, 0x00, 1);
if (rc) {
pr_err("failed to set OTP cfg\n");
return -EIO;
}
}
intf_ctl = (write ? INTF_CTL_WR_EN : 0) | (burst ? INTF_CTL_BURST : 0);
rc = fg_write(chip, &intf_ctl, MEM_INTF_CTL(chip), 1);
if (rc) {
pr_err("failed to set mem access bit\n");
return -EIO;
}
return rc;
}
static int fg_req_and_wait_access(struct fg_chip *chip, int timeout)
{
int rc = 0, ret = 0;
bool tried_again = false;
if (!fg_check_sram_access(chip)) {
rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ, 1);
if (rc) {
pr_err("failed to set mem access bit\n");
return -EIO;
}
fg_stay_awake(&chip->memif_wakeup_source);
}
wait:
/* Wait for MEM_AVAIL IRQ. */
ret = wait_for_completion_interruptible_timeout(
&chip->sram_access_granted,
msecs_to_jiffies(timeout));
/* If we were interrupted wait again one more time. */
if (ret == -ERESTARTSYS && !tried_again) {
tried_again = true;
goto wait;
} else if (ret <= 0) {
rc = -ETIMEDOUT;
pr_err("transaction timed out rc=%d\n", rc);
return rc;
}
return rc;
}
static int fg_release_access(struct fg_chip *chip)
{
int rc;
rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
RIF_MEM_ACCESS_REQ, 0, 1);
fg_relax(&chip->memif_wakeup_source);
INIT_COMPLETION(chip->sram_access_granted);
return rc;
}
static void fg_release_access_if_necessary(struct fg_chip *chip)
{
mutex_lock(&chip->rw_lock);
if (atomic_sub_return(1, &chip->memif_user_cnt) <= 0) {
fg_release_access(chip);
}
mutex_unlock(&chip->rw_lock);
}
/*
* fg_mem_lock disallows the fuel gauge to release access until it has been
* released.
*
* an equal number of calls must be made to fg_mem_release for the fuel gauge
* driver to release the sram access.
*/
static void fg_mem_lock(struct fg_chip *chip)
{
mutex_lock(&chip->rw_lock);
atomic_add_return(1, &chip->memif_user_cnt);
mutex_unlock(&chip->rw_lock);
}
static void fg_mem_release(struct fg_chip *chip)
{
fg_release_access_if_necessary(chip);
}
static int fg_set_ram_addr(struct fg_chip *chip, u16 *address)
{
int rc;
rc = fg_write(chip, (u8 *) address,
chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], 2);
if (rc) {
pr_err("spmi write failed: addr=%03X, rc=%d\n",
chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], rc);
return rc;
}
return rc;
}
#define BUF_LEN 4
static int fg_sub_mem_read(struct fg_chip *chip, u8 *val, u16 address, int len,
int offset)
{
int rc, total_len;
u8 *rd_data = val;
bool otp;
char str[DEBUG_PRINT_BUFFER_SIZE];
if (address < RAM_OFFSET)
otp = true;
rc = fg_config_access(chip, 0, (len > 4), otp);
if (rc)
return rc;
rc = fg_set_ram_addr(chip, &address);
if (rc)
return rc;
if (fg_debug_mask & FG_MEM_DEBUG_READS)
pr_info("length %d addr=%02X\n", len, address);
total_len = len;
while (len > 0) {
if (!offset) {
rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip),
min(len, BUF_LEN));
} else {
rc = fg_read(chip, rd_data,
MEM_INTF_RD_DATA0(chip) + offset,
min(len, BUF_LEN - offset));
/* manually set address to allow continous reads */
address += BUF_LEN;
rc = fg_set_ram_addr(chip, &address);
if (rc)
return rc;
}
if (rc) {
pr_err("spmi read failed: addr=%03x, rc=%d\n",
MEM_INTF_RD_DATA0(chip) + offset, rc);
return rc;
}
rd_data += (BUF_LEN - offset);
len -= (BUF_LEN - offset);
offset = 0;
}
if (fg_debug_mask & FG_MEM_DEBUG_READS) {
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len);
pr_info("data: %s\n", str);
}
return rc;
}
static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address,
int len, int offset, bool keep_access)
{
int rc = 0, user_cnt = 0, orig_address = address;
if (offset > 3) {
pr_err("offset too large %d\n", offset);
return -EINVAL;
}
address = ((orig_address + offset) / 4) * 4;
offset = (orig_address + offset) % 4;
user_cnt = atomic_add_return(1, &chip->memif_user_cnt);
if (fg_debug_mask & FG_MEM_DEBUG_READS)
pr_info("user_cnt %d\n", user_cnt);
mutex_lock(&chip->rw_lock);
if (!fg_check_sram_access(chip)) {
rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS);
if (rc)
goto out;
}
rc = fg_sub_mem_read(chip, val, address, len, offset);
out:
user_cnt = atomic_sub_return(1, &chip->memif_user_cnt);
if (fg_debug_mask & FG_MEM_DEBUG_READS)
pr_info("user_cnt %d\n", user_cnt);
fg_assert_sram_access(chip);
if (!keep_access && (user_cnt == 0) && !rc) {
rc = fg_release_access(chip);
if (rc) {
pr_err("failed to set mem access bit\n");
rc = -EIO;
}
}
mutex_unlock(&chip->rw_lock);
return rc;
}
static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address,
int len, int offset, bool keep_access)
{
int rc = 0, user_cnt = 0, sublen;
bool access_configured = false;
u8 *wr_data = val, word[4];
char str[DEBUG_PRINT_BUFFER_SIZE];
if (address < RAM_OFFSET)
return -EINVAL;
if (offset > 3)
return -EINVAL;
address = ((address + offset) / 4) * 4;
offset = (address + offset) % 4;
user_cnt = atomic_add_return(1, &chip->memif_user_cnt);
if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
pr_info("user_cnt %d\n", user_cnt);
mutex_lock(&chip->rw_lock);
if (!fg_check_sram_access(chip)) {
rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS);
if (rc)
goto out;
}
if (fg_debug_mask & FG_MEM_DEBUG_WRITES) {
pr_info("length %d addr=%02X offset=%d\n",
len, address, offset);
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, wr_data, len);
pr_info("writing: %s\n", str);
}
while (len > 0) {
if (offset != 0) {
sublen = min(4 - offset, len);
rc = fg_sub_mem_read(chip, word, address, 4, 0);
if (rc)
goto out;
memcpy(word + offset, wr_data, sublen);
/* configure access as burst if more to write */
rc = fg_config_access(chip, 1, (len - sublen) > 0, 0);
if (rc)
goto out;
rc = fg_set_ram_addr(chip, &address);
if (rc)
goto out;
offset = 0;
access_configured = true;
} else if (len >= 4) {
if (!access_configured) {
rc = fg_config_access(chip, 1, len > 4, 0);
if (rc)
goto out;
rc = fg_set_ram_addr(chip, &address);
if (rc)
goto out;
access_configured = true;
}
sublen = 4;
memcpy(word, wr_data, 4);
} else if (len > 0 && len < 4) {
sublen = len;
rc = fg_sub_mem_read(chip, word, address, 4, 0);
if (rc)
goto out;
memcpy(word, wr_data, sublen);
rc = fg_config_access(chip, 1, 0, 0);
if (rc)
goto out;
rc = fg_set_ram_addr(chip, &address);
if (rc)
goto out;
access_configured = true;
} else {
pr_err("Invalid length: %d\n", len);
break;
}
rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip), 4);
if (rc) {
pr_err("spmi write failed: addr=%03x, rc=%d\n",
MEM_INTF_WR_DATA0(chip), rc);
goto out;
}
len -= sublen;
wr_data += sublen;
address += 4;
}
out:
user_cnt = atomic_sub_return(1, &chip->memif_user_cnt);
if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
pr_info("user_cnt %d\n", user_cnt);
fg_assert_sram_access(chip);
if (!keep_access && (user_cnt == 0) && !rc) {
rc = fg_release_access(chip);
if (rc) {
pr_err("failed to set mem access bit\n");
rc = -EIO;
}
}
mutex_unlock(&chip->rw_lock);
return rc;
}
static int fg_mem_masked_write(struct fg_chip *chip, u16 addr,
u8 mask, u8 val, u8 offset)
{
int rc = 0;
u8 reg[4];
char str[DEBUG_PRINT_BUFFER_SIZE];
rc = fg_mem_read(chip, reg, addr, 4, 0, 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc);
return rc;
}
reg[offset] &= ~mask;
reg[offset] |= val & mask;
str[0] = '\0';
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, reg, 4);
pr_debug("Writing %s address %03x, offset %d\n", str, addr, offset);
rc = fg_mem_write(chip, reg, addr, 4, 0, 0);
if (rc) {
pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
return rc;
}
return rc;
}
static int soc_to_setpoint(int soc)
{
return DIV_ROUND_CLOSEST(soc * 255, 100);
}
static void batt_to_setpoint_adc(int vbatt_mv, u8 *data)
{
int val;
/* Battery voltage is an offset from 0 V and LSB is 1/2^15. */
val = DIV_ROUND_CLOSEST(vbatt_mv * 32768, 5000);
data[0] = val & 0xFF;
data[1] = val >> 8;
return;
}
static u8 batt_to_setpoint_8b(int vbatt_mv)
{
int val;
/* Battery voltage is an offset from 2.5 V and LSB is 5/2^9. */
val = (vbatt_mv - 2500) * 512 / 1000;
return DIV_ROUND_CLOSEST(val, 5);
}
static int get_current_time(unsigned long *now_tm_sec)
{
struct rtc_time tm;
struct rtc_device *rtc;
int rc;
rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
if (rtc == NULL) {
pr_err("%s: unable to open rtc device (%s)\n",
__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
return -EINVAL;
}
rc = rtc_read_time(rtc, &tm);
if (rc) {
pr_err("Error reading rtc device (%s) : %d\n",
CONFIG_RTC_HCTOSYS_DEVICE, rc);
goto close_time;
}
rc = rtc_valid_tm(&tm);
if (rc) {
pr_err("Invalid RTC time (%s): %d\n",
CONFIG_RTC_HCTOSYS_DEVICE, rc);
goto close_time;
}
rtc_tm_to_time(&tm, now_tm_sec);
close_time:
rtc_class_close(rtc);
return rc;
}
#define BATTERY_SOC_REG 0x56C
#define BATTERY_SOC_OFFSET 1
#define FULL_PERCENT_3B 0xFFFFFF
static int get_battery_soc_raw(struct fg_chip *chip)
{
int rc;
u8 buffer[3];
rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0);
if (rc) {
pr_err("Unable to read battery soc: %d\n", rc);
return 0;
}
return (int)(buffer[2] << 16 | buffer[1] << 8 | buffer[0]);
}
#define COUNTER_IMPTR_REG 0X558
#define COUNTER_PULSE_REG 0X55C
#define SOC_FULL_REG 0x564
#define COUNTER_IMPTR_OFFSET 2
#define COUNTER_PULSE_OFFSET 0
#define SOC_FULL_OFFSET 3
#define ESR_PULSE_RECONFIG_SOC 0xFFF971
static int fg_configure_soc(struct fg_chip *chip)
{
u32 batt_soc;
u8 cntr[2] = {0, 0};
int rc = 0;
mutex_lock(&chip->rw_lock);
atomic_add_return(1, &chip->memif_user_cnt);
mutex_unlock(&chip->rw_lock);
/* Read Battery SOC */
batt_soc = get_battery_soc_raw(chip);
if (batt_soc > ESR_PULSE_RECONFIG_SOC) {
if (fg_debug_mask & FG_POWER_SUPPLY)
pr_info("Configuring soc registers batt_soc: %x\n",
batt_soc);
batt_soc = ESR_PULSE_RECONFIG_SOC;
rc = fg_mem_write(chip, (u8 *)&batt_soc, BATTERY_SOC_REG, 3,
BATTERY_SOC_OFFSET, 1);
if (rc) {
pr_err("failed to write BATT_SOC rc=%d\n", rc);
goto out;
}
rc = fg_mem_write(chip, (u8 *)&batt_soc, SOC_FULL_REG, 3,
SOC_FULL_OFFSET, 1);
if (rc) {
pr_err("failed to write SOC_FULL rc=%d\n", rc);
goto out;
}
rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 2,
COUNTER_IMPTR_OFFSET, 1);
if (rc) {
pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
goto out;
}
rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2,
COUNTER_PULSE_OFFSET, 0);
if (rc)
pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
}
out:
fg_release_access_if_necessary(chip);
return rc;
}
#define SOC_EMPTY BIT(3)
static bool fg_is_batt_empty(struct fg_chip *chip)
{
u8 fg_soc_sts;
int rc;
rc = fg_read(chip, &fg_soc_sts,
INT_RT_STS(chip->soc_base), 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n",
INT_RT_STS(chip->soc_base), rc);
return false;
}
return (fg_soc_sts & SOC_EMPTY) != 0;
}
static int get_monotonic_soc_raw(struct fg_chip *chip)
{
u8 cap[2];
int rc, tries = 0;
while (tries < MAX_TRIES_SOC) {
rc = fg_read(chip, cap,
chip->soc_base + SOC_MONOTONIC_SOC, 2);
if (rc) {
pr_err("spmi read failed: addr=%03x, rc=%d\n",
chip->soc_base + SOC_MONOTONIC_SOC, rc);
return rc;
}
if (cap[0] == cap[1])
break;
tries++;
}
if (tries == MAX_TRIES_SOC) {
pr_err("shadow registers do not match\n");
return -EINVAL;
}
if (fg_debug_mask & FG_POWER_SUPPLY)
pr_info_ratelimited("raw: 0x%02x\n", cap[0]);
return cap[0];
}
#define EMPTY_CAPACITY 0
#define DEFAULT_CAPACITY 50
#define MISSING_CAPACITY 100
#define FULL_CAPACITY 100
#define FULL_SOC_RAW 0xFF
static int get_prop_capacity(struct fg_chip *chip)
{
int msoc;
if (chip->battery_missing)
return MISSING_CAPACITY;
if (!chip->profile_loaded && !chip->use_otp_profile)
return DEFAULT_CAPACITY;
if (chip->charge_full)
return FULL_CAPACITY;
if (chip->soc_empty) {
if (fg_debug_mask & FG_POWER_SUPPLY)
pr_info_ratelimited("capacity: %d, EMPTY\n",
EMPTY_CAPACITY);
return EMPTY_CAPACITY;
}
msoc = get_monotonic_soc_raw(chip);
if (msoc == 0)
return EMPTY_CAPACITY;
else if (msoc == FULL_SOC_RAW)
return FULL_CAPACITY;
return DIV_ROUND_CLOSEST((msoc - 1) * (FULL_CAPACITY - 1),
FULL_SOC_RAW - 2) + 1;
}
#define HIGH_BIAS 3
#define MED_BIAS BIT(1)
#define LOW_BIAS BIT(0)
static u8 bias_ua[] = {
[HIGH_BIAS] = 150,
[MED_BIAS] = 15,
[LOW_BIAS] = 5,
};
static int64_t get_batt_id(unsigned int battery_id_uv, u8 bid_info)
{
u64 battery_id_ohm;
if ((bid_info & 0x3) == 0) {
pr_err("can't determine battery id 0x%02x\n", bid_info);
return -EINVAL;
}
battery_id_ohm = div_u64(battery_id_uv, bias_ua[bid_info & 0x3]);
return battery_id_ohm;
}
#define DEFAULT_TEMP_DEGC 250
static int get_sram_prop_now(struct fg_chip *chip, unsigned int type)
{
if (fg_debug_mask & FG_POWER_SUPPLY)
pr_info("addr 0x%02X, offset %d value %d\n",
fg_data[type].address, fg_data[type].offset,
fg_data[type].value);
if (type == FG_DATA_BATT_ID)
return get_batt_id(fg_data[type].value,
fg_data[FG_DATA_BATT_ID_INFO].value);
return fg_data[type].value;
}
#define MIN_TEMP_DEGC -300
#define MAX_TEMP_DEGC 970
static int get_prop_jeita_temp(struct fg_chip *chip, unsigned int type)
{
if (fg_debug_mask & FG_POWER_SUPPLY)
pr_info("addr 0x%02X, offset %d\n", settings[type].address,
settings[type].offset);
return settings[type].value;
}
static int set_prop_jeita_temp(struct fg_chip *chip,
unsigned int type, int decidegc)
{
int rc = 0;
if (fg_debug_mask & FG_POWER_SUPPLY)
pr_info("addr 0x%02X, offset %d temp%d\n",
settings[type].address,
settings[type].offset, decidegc);
settings[type].value = decidegc;
cancel_delayed_work_sync(
&chip->update_jeita_setting);
schedule_delayed_work(
&chip->update_jeita_setting, 0);
return rc;
}
#define EXTERNAL_SENSE_SELECT 0x4AC
#define EXTERNAL_SENSE_OFFSET 0x2
#define EXTERNAL_SENSE_BIT BIT(2)
static int set_prop_sense_type(struct fg_chip *chip, int ext_sense_type)
{
int rc;
rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
EXTERNAL_SENSE_BIT,
ext_sense_type ? EXTERNAL_SENSE_BIT : 0,
EXTERNAL_SENSE_OFFSET);
if (rc) {
pr_err("failed to write profile rc=%d\n", rc);
return rc;
}
return 0;
}
#define EXPONENT_MASK 0xF800
#define MANTISSA_MASK 0x3FF
#define SIGN BIT(10)
#define EXPONENT_SHIFT 11
#define MICRO_UNIT 1000000ULL
static int64_t float_decode(u16 reg)
{
int64_t final_val, exponent_val, mantissa_val;
int exponent, mantissa, n;
bool sign;
exponent = (reg & EXPONENT_MASK) >> EXPONENT_SHIFT;
mantissa = (reg & MANTISSA_MASK);
sign = !!(reg & SIGN);
pr_debug("exponent=%d mantissa=%d sign=%d\n", exponent, mantissa, sign);
mantissa_val = mantissa * MICRO_UNIT;
n = exponent - 15;
if (n < 0)
exponent_val = MICRO_UNIT >> -n;
else
exponent_val = MICRO_UNIT << n;
n = n - 10;
if (n < 0)
mantissa_val >>= -n;
else
mantissa_val <<= n;
final_val = exponent_val + mantissa_val;
if (sign)
final_val *= -1;
return final_val;
}
#define MIN_HALFFLOAT_EXP_N -15
#define MAX_HALFFLOAT_EXP_N 16
static int log2_floor(int64_t uval)
{
int n = 0;
int64_t i = MICRO_UNIT;
if (uval > i) {
while (uval > i && n > MIN_HALFFLOAT_EXP_N) {
i <<= 1;
n += 1;
}
if (uval < i)
n -= 1;
} else if (uval < i) {
while (uval < i && n < MAX_HALFFLOAT_EXP_N) {
i >>= 1;
n -= 1;
}
}
return n;
}
static int64_t exp2_int(int64_t n)
{
int p = n - 1;
if (p > 0)
return (2 * MICRO_UNIT) << p;
else
return (2 * MICRO_UNIT) >> abs(p);
}
static u16 float_encode(int64_t uval)
{
int sign = 0, n, exp, mantissa;
u16 half = 0;
if (uval < 0) {
sign = 1;
uval = abs(uval);
}
n = log2_floor(uval);
exp = n + 15;
mantissa = div_s64(div_s64((uval - exp2_int(n)) * exp2_int(10 - n),
MICRO_UNIT) + MICRO_UNIT / 2, MICRO_UNIT);
half = (mantissa & MANTISSA_MASK) | ((sign << 10) & SIGN)
| ((exp << 11) & EXPONENT_MASK);
if (fg_debug_mask & FG_STATUS)
pr_info("uval = %lld, m = 0x%02x, sign = 0x%02x, exp = 0x%02x, half = 0x%04x\n",
uval, mantissa, sign, exp, half);
return half;
}
#define BATT_IDED BIT(3)
static int fg_is_batt_id_valid(struct fg_chip *chip)
{
u8 fg_batt_sts;
int rc;
rc = fg_read(chip, &fg_batt_sts,
INT_RT_STS(chip->batt_base), 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n",
INT_RT_STS(chip->batt_base), rc);
return rc;
}
if (fg_debug_mask & FG_IRQS)
pr_info("fg batt sts 0x%x\n", fg_batt_sts);
return (fg_batt_sts & BATT_IDED) ? 1 : 0;
}
static int64_t twos_compliment_extend(int64_t val, int nbytes)
{
int i;
int64_t mask;
mask = 0x80LL << ((nbytes - 1) * 8);
if (val & mask) {
for (i = 8; i > nbytes; i--) {
mask = 0xFFLL << ((i - 1) * 8);
val |= mask;
}
}
return val;
}
#define LSB_16B_NUMRTR 152587
#define LSB_16B_DENMTR 1000
#define LSB_8B 9800
#define TEMP_LSB_16B 625
#define DECIKELVIN 2730
#define SRAM_PERIOD_NO_ID_UPDATE_MS 100
#define FULL_PERCENT_28BIT 0xFFFFFFF
static void update_sram_data(struct fg_chip *chip, int *resched_ms)
{
int i, j, rc = 0;
u8 reg[4];
int64_t temp;
int battid_valid = fg_is_batt_id_valid(chip);
union power_supply_propval prop = {0, };
fg_stay_awake(&chip->update_sram_wakeup_source);
fg_mem_lock(chip);
for (i = 1; i < FG_DATA_MAX; i++) {
if (chip->profile_loaded && i >= FG_DATA_BATT_ID)
continue;
rc = fg_mem_read(chip, reg, fg_data[i].address,
fg_data[i].len, fg_data[i].offset, 0);
if (rc) {
pr_err("Failed to update sram data\n");
break;
}
temp = 0;
for (j = 0; j < fg_data[i].len; j++)
temp |= reg[j] << (8 * j);
switch (i) {
case FG_DATA_OCV:
case FG_DATA_VOLTAGE:
case FG_DATA_CPRED_VOLTAGE:
fg_data[i].value = div_u64(
(u64)(u16)temp * LSB_16B_NUMRTR,
LSB_16B_DENMTR);
break;
case FG_DATA_CURRENT:
temp = twos_compliment_extend(temp, fg_data[i].len);
fg_data[i].value = div_s64(
(s64)temp * LSB_16B_NUMRTR,
LSB_16B_DENMTR);
break;
case FG_DATA_BATT_ESR:
fg_data[i].value = float_decode((u16) temp);
break;
case FG_DATA_BATT_ESR_COUNT:
fg_data[i].value = (u16)temp;
break;
case FG_DATA_BATT_ID:
if (battid_valid)
fg_data[i].value = reg[0] * LSB_8B;
break;
case FG_DATA_BATT_ID_INFO:
if (battid_valid)
fg_data[i].value = reg[0];
break;
case FG_DATA_BATT_SOC:
fg_data[i].value = div64_s64((temp * 10000),
FULL_PERCENT_3B);
break;
case FG_DATA_CC_CHARGE:
temp = twos_compliment_extend(temp, fg_data[i].len);
fg_data[i].value = div64_s64(
temp * (int64_t)chip->nom_cap_uah,
FULL_PERCENT_28BIT);
break;
case FG_DATA_VINT_ERR:
temp = twos_compliment_extend(temp, fg_data[i].len);
fg_data[i].value = div64_s64(temp * chip->nom_cap_uah,
FULL_PERCENT_3B);
break;
};
if (fg_debug_mask & FG_MEM_DEBUG_READS)
pr_info("%d %lld %d\n", i, temp, fg_data[i].value);
}
fg_mem_release(chip);
if (!rc)
get_current_time(&chip->last_sram_update_time);
if (battid_valid) {
complete_all(&chip->batt_id_avail);
*resched_ms = fg_sram_update_period_ms;
} else {
*resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS;
}
/* Update compass compensation when charge related parameters changes */
if (chip->batt_psy)
chip->batt_psy->set_property(chip->batt_psy,
POWER_SUPPLY_PROP_COMPASS_COMPENSATION, &prop);
fg_relax(&chip->update_sram_wakeup_source);
}
#define SRAM_TIMEOUT_MS 3000
static void update_sram_data_work(struct work_struct *work)
{
struct fg_chip *chip = container_of(work,
struct fg_chip,
update_sram_data.work);
int resched_ms, ret;
bool tried_again = false;
wait:
/* Wait for MEMIF access revoked */
ret = wait_for_completion_interruptible_timeout(
&chip->sram_access_revoked,
msecs_to_jiffies(SRAM_TIMEOUT_MS));
/* If we were interrupted wait again one more time. */
if (ret == -ERESTARTSYS && !tried_again) {
tried_again = true;
goto wait;
} else if (ret <= 0) {
pr_err("transaction timed out ret=%d\n", ret);
goto out;
}
update_sram_data(chip, &resched_ms);
out:
schedule_delayed_work(
&chip->update_sram_data,
msecs_to_jiffies(resched_ms));
}
#define BATT_TEMP_OFFSET 3
#define BATT_TEMP_CNTRL_MASK 0x17
#define DISABLE_THERM_BIT BIT(0)
#define TEMP_SENSE_ALWAYS_BIT BIT(1)
#define TEMP_SENSE_CHARGE_BIT BIT(2)
#define FORCE_RBIAS_ON_BIT BIT(4)
#define BATT_TEMP_OFF DISABLE_THERM_BIT
#define BATT_TEMP_ON (FORCE_RBIAS_ON_BIT | TEMP_SENSE_ALWAYS_BIT | \
TEMP_SENSE_CHARGE_BIT)
#define TEMP_PERIOD_UPDATE_MS 10000
#define TEMP_PERIOD_TIMEOUT_MS 3000
static void update_temp_data(struct work_struct *work)
{
s16 temp;
u8 reg[2];
bool tried_again = false;
int rc, ret, timeout = TEMP_PERIOD_TIMEOUT_MS;
struct fg_chip *chip = container_of(work,
struct fg_chip,
update_temp_work.work);
fg_stay_awake(&chip->update_temp_wakeup_source);
if (chip->sw_rbias_ctrl) {
rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
BATT_TEMP_CNTRL_MASK,
BATT_TEMP_ON,
BATT_TEMP_OFFSET);
if (rc) {
pr_err("failed to write BATT_TEMP_ON rc=%d\n", rc);
goto out;
}
wait:
/* Wait for MEMIF access revoked */
ret = wait_for_completion_interruptible_timeout(
&chip->sram_access_revoked,
msecs_to_jiffies(timeout));
/* If we were interrupted wait again one more time. */
if (ret == -ERESTARTSYS && !tried_again) {
tried_again = true;
goto wait;
} else if (ret <= 0) {
rc = -ETIMEDOUT;
pr_err("transaction timed out ret=%d\n", ret);
goto out;
}
}
/* Read FG_DATA_BATT_TEMP now */
rc = fg_mem_read(chip, reg, fg_data[0].address,
fg_data[0].len, fg_data[0].offset,
chip->sw_rbias_ctrl ? 1 : 0);
if (rc) {
pr_err("Failed to update temp data\n");
goto out;
}
temp = reg[0] | (reg[1] << 8);
fg_data[0].value = (temp * TEMP_LSB_16B / 1000)
- DECIKELVIN;
if (fg_debug_mask & FG_MEM_DEBUG_READS)
pr_info("BATT_TEMP %d %d\n", temp, fg_data[0].value);
get_current_time(&chip->last_temp_update_time);
out:
if (chip->sw_rbias_ctrl) {
rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
BATT_TEMP_CNTRL_MASK,
BATT_TEMP_OFF,
BATT_TEMP_OFFSET);
if (rc)
pr_err("failed to write BATT_TEMP_OFF rc=%d\n", rc);
}
schedule_delayed_work(
&chip->update_temp_work,
msecs_to_jiffies(TEMP_PERIOD_UPDATE_MS));
fg_relax(&chip->update_temp_wakeup_source);
}
static void update_jeita_setting(struct work_struct *work)
{
struct fg_chip *chip = container_of(work,
struct fg_chip,
update_jeita_setting.work);
u8 reg[4];
int i, rc;
for (i = 0; i < 4; i++)
reg[i] = (settings[FG_MEM_SOFT_COLD + i].value / 10) + 30;
rc = fg_mem_write(chip, reg, settings[FG_MEM_SOFT_COLD].address,
4, settings[FG_MEM_SOFT_COLD].offset, 0);
if (rc)
pr_err("failed to update JEITA setting rc=%d\n", rc);
}
static int fg_set_resume_soc(struct fg_chip *chip, u8 threshold)
{
u16 address;
int offset, rc;
address = settings[FG_MEM_RESUME_SOC].address;
offset = settings[FG_MEM_RESUME_SOC].offset;
rc = fg_mem_masked_write(chip, address, 0xFF, threshold, offset);
if (rc)
pr_err("write failed rc=%d\n", rc);
else
pr_debug("setting resume-soc to %x\n", threshold);
return rc;
}
#define VBATT_LOW_STS_BIT BIT(2)
static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts)
{
int rc = 0;
u8 fg_batt_sts;
rc = fg_read(chip, &fg_batt_sts, INT_RT_STS(chip->batt_base), 1);
if (!rc)
*vbatt_low_sts = !!(fg_batt_sts & VBATT_LOW_STS_BIT);
return rc;
}
#define BATT_CYCLE_NUMBER_REG 0x5E8
#define BATT_CYCLE_OFFSET 0
static void restore_cycle_counter(struct fg_chip *chip)
{
int rc = 0, i, address;
u8 data[2];
fg_mem_lock(chip);
for (i = 0; i < BUCKET_COUNT; i++) {
address = BATT_CYCLE_NUMBER_REG + i * 2;
rc = fg_mem_read(chip, (u8 *)&data, address, 2,
BATT_CYCLE_OFFSET, 0);
if (rc)
pr_err("Failed to read BATT_CYCLE_NUMBER[%d] rc: %d\n",
i, rc);
else
chip->cyc_ctr.count[i] = data[0] | data[1] << 8;
}
fg_mem_release(chip);
}
static void clear_cycle_counter(struct fg_chip *chip)
{
int rc = 0, len, i;
if (!chip->cyc_ctr.en)
return;
len = sizeof(chip->cyc_ctr.count);
memset(chip->cyc_ctr.count, 0, len);
for (i = 0; i < BUCKET_COUNT; i++) {
chip->cyc_ctr.started[i] = false;
chip->cyc_ctr.last_soc[i] = 0;
}
rc = fg_mem_write(chip, (u8 *)&chip->cyc_ctr.count,
BATT_CYCLE_NUMBER_REG, len,
BATT_CYCLE_OFFSET, 0);
if (rc)
pr_err("failed to write BATT_CYCLE_NUMBER rc=%d\n", rc);
}
static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket)
{
int rc = 0, address;
u16 cyc_count;
u8 data[2];
if (bucket < 0 || (bucket > BUCKET_COUNT - 1))
return 0;
cyc_count = chip->cyc_ctr.count[bucket];
cyc_count++;
data[0] = cyc_count & 0xFF;
data[1] = cyc_count >> 8;
address = BATT_CYCLE_NUMBER_REG + bucket * 2;
rc = fg_mem_write(chip, data, address, 2, BATT_CYCLE_OFFSET, 0);
if (rc)
pr_err("failed to write BATT_CYCLE_NUMBER[%d] rc=%d\n",
bucket, rc);
else
chip->cyc_ctr.count[bucket] = cyc_count;
return rc;
}
static void update_cycle_count(struct work_struct *work)
{
int rc = 0, bucket, i;
u8 reg[3], batt_soc;
struct fg_chip *chip = container_of(work,
struct fg_chip,
cycle_count_work);
mutex_lock(&chip->cyc_ctr.lock);
rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3,
BATTERY_SOC_OFFSET, 0);
if (rc) {
pr_err("Failed to read battery soc rc: %d\n", rc);
goto out;
}
batt_soc = reg[2];
if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
/* Find out which bucket the SOC falls in */
bucket = batt_soc / BUCKET_SOC_PCT;
if (fg_debug_mask & FG_STATUS)
pr_info("batt_soc: %x bucket: %d\n", reg[2], bucket);
/*
* If we've started counting for the previous bucket,
* then store the counter for that bucket if the
* counter for current bucket is getting started.
*/
if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] &&
!chip->cyc_ctr.started[bucket]) {
rc = fg_inc_store_cycle_ctr(chip, bucket - 1);
if (rc) {
pr_err("Error in storing cycle_ctr rc: %d\n",
rc);
goto out;
} else {
chip->cyc_ctr.started[bucket - 1] = false;
chip->cyc_ctr.last_soc[bucket - 1] = 0;
}
}
if (!chip->cyc_ctr.started[bucket]) {
chip->cyc_ctr.started[bucket] = true;
chip->cyc_ctr.last_soc[bucket] = batt_soc;
}
} else {
for (i = 0; i < BUCKET_COUNT; i++) {
if (chip->cyc_ctr.started[i] &&
batt_soc > chip->cyc_ctr.last_soc[i]) {
rc = fg_inc_store_cycle_ctr(chip, i);
if (rc)
pr_err("Error in storing cycle_ctr rc: %d\n",
rc);
chip->cyc_ctr.last_soc[i] = 0;
}
chip->cyc_ctr.started[i] = false;
}
}
out:
mutex_unlock(&chip->cyc_ctr.lock);
}
static int fg_get_cycle_count(struct fg_chip *chip)
{
int count;
if (!chip->cyc_ctr.en)
return 0;
if ((chip->cyc_ctr.id <= 0) || (chip->cyc_ctr.id > BUCKET_COUNT))
return -EINVAL;
mutex_lock(&chip->cyc_ctr.lock);
count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1];
mutex_unlock(&chip->cyc_ctr.lock);
return count;
}
static void half_float_to_buffer(int64_t uval, u8 *buffer)
{
u16 raw;
raw = float_encode(uval);
buffer[0] = (u8)(raw & 0xFF);
buffer[1] = (u8)((raw >> 8) & 0xFF);
}
static int64_t half_float(u8 *buffer)
{
u16 val;
val = buffer[1] << 8 | buffer[0];
return float_decode(val);
}
static int voltage_2b(u8 *buffer)
{
u16 val;
val = buffer[1] << 8 | buffer[0];
/* the range of voltage 2b is [-5V, 5V], so it will fit in an int */
return (int)div_u64(((u64)val) * LSB_16B_NUMRTR, LSB_16B_DENMTR);
}
static int bcap_uah_2b(u8 *buffer)
{
u16 val;
val = buffer[1] << 8 | buffer[0];
return ((int)val) * 1000;
}
static int lookup_ocv_for_soc(struct fg_chip *chip, int soc)
{
int64_t *coeffs;
if (soc > chip->ocv_junction_p1p2 * 10)
coeffs = chip->ocv_coeffs;
else if (soc > chip->ocv_junction_p2p3 * 10)
coeffs = chip->ocv_coeffs + 4;
else
coeffs = chip->ocv_coeffs + 8;
/* the range of ocv will fit in a 32 bit int */
return (int)(coeffs[0]
+ div_s64(coeffs[1] * soc, 1000LL)
+ div_s64(coeffs[2] * soc * soc, 1000000LL)
+ div_s64(coeffs[3] * soc * soc * soc, 1000000000LL));
}
static int lookup_soc_for_ocv(struct fg_chip *chip, int ocv)
{
int64_t val;
int soc = -EINVAL;
/*
* binary search variables representing the valid start and end
* percentages to search
*/
int start = 0, end = 1000, mid;
if (fg_debug_mask & FG_AGING)
pr_info("target_ocv = %d\n", ocv);
/* do a binary search for the closest soc to match the ocv */
while (end - start > 1) {
mid = (start + end) / 2;
val = lookup_ocv_for_soc(chip, mid);
if (fg_debug_mask & FG_AGING)
pr_info("start = %d, mid = %d, end = %d, ocv = %lld\n",
start, mid, end, val);
if (ocv < val) {
end = mid;
} else if (ocv > val) {
start = mid;
} else {
soc = mid;
break;
}
}
/*
* if the exact soc was not found and there are two or less values
* remaining, just compare them and see which one is closest to the ocv
*/
if (soc == -EINVAL) {
if (abs(ocv - lookup_ocv_for_soc(chip, start))
> abs(ocv - lookup_ocv_for_soc(chip, end)))
soc = end;
else
soc = start;
}
if (fg_debug_mask & FG_AGING)
pr_info("closest = %d, target_ocv = %d, ocv_found = %d\n",
soc, ocv, lookup_ocv_for_soc(chip, soc));
return soc;
}
#define ESR_ACTUAL_REG 0x554
#define BATTERY_ESR_REG 0x4F4
#define TEMP_RS_TO_RSLOW_REG 0x514
static int estimate_battery_age(struct fg_chip *chip, int *actual_capacity)
{
int64_t ocv_cutoff_new, ocv_cutoff_aged, temp_rs_to_rslow;
int64_t esr_actual, battery_esr, val;
int soc_cutoff_aged, soc_cutoff_new, rc;
int battery_soc, unusable_soc, batt_temp;
u8 buffer[3];
if (chip->batt_aging_mode != FG_AGING_ESR)
return 0;
if (chip->nom_cap_uah == 0) {
if (fg_debug_mask & FG_AGING)
pr_info("ocv coefficients not loaded, aborting\n");
return 0;
}
fg_mem_lock(chip);
batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
if (batt_temp < 150 || batt_temp > 400) {
if (fg_debug_mask & FG_AGING)
pr_info("Battery temp (%d) out of range, aborting\n",
(int)batt_temp);
rc = 0;
goto done;
}
battery_soc = get_battery_soc_raw(chip) * 100 / FULL_PERCENT_3B;
if (rc) {
goto error_done;
} else if (battery_soc < 25 || battery_soc > 75) {
if (fg_debug_mask & FG_AGING)
pr_info("Battery SoC (%d) out of range, aborting\n",
(int)battery_soc);
rc = 0;
goto done;
}
rc = fg_mem_read(chip, buffer, ESR_ACTUAL_REG, 2, 2, 0);
esr_actual = half_float(buffer);
rc |= fg_mem_read(chip, buffer, BATTERY_ESR_REG, 2, 2, 0);
battery_esr = half_float(buffer);
if (rc) {
goto error_done;
} else if (esr_actual < battery_esr) {
if (fg_debug_mask & FG_AGING)
pr_info("Batt ESR lower than ESR actual, aborting\n");
rc = 0;
goto done;
}
rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2, 0, 0);
temp_rs_to_rslow = half_float(buffer);
if (rc)
goto error_done;
fg_mem_release(chip);
if (fg_debug_mask & FG_AGING) {
pr_info("batt_soc = %d, cutoff_voltage = %lld, eval current = %d\n",
battery_soc, chip->cutoff_voltage,
chip->evaluation_current);
pr_info("temp_rs_to_rslow = %lld, batt_esr = %lld, esr_actual = %lld\n",
temp_rs_to_rslow, battery_esr, esr_actual);
}
/* calculate soc_cutoff_new */
val = (1000000LL + temp_rs_to_rslow) * battery_esr;
do_div(val, 1000000);
ocv_cutoff_new = div64_s64(chip->evaluation_current * val, 1000)
+ chip->cutoff_voltage;
/* calculate soc_cutoff_aged */
val = (1000000LL + temp_rs_to_rslow) * esr_actual;
do_div(val, 1000000);
ocv_cutoff_aged = div64_s64(chip->evaluation_current * val, 1000)
+ chip->cutoff_voltage;
if (fg_debug_mask & FG_AGING)
pr_info("ocv_cutoff_new = %lld, ocv_cutoff_aged = %lld\n",
ocv_cutoff_new, ocv_cutoff_aged);
soc_cutoff_new = lookup_soc_for_ocv(chip, ocv_cutoff_new);
soc_cutoff_aged = lookup_soc_for_ocv(chip, ocv_cutoff_aged);
if (fg_debug_mask & FG_AGING)
pr_info("aged soc = %d, new soc = %d\n",
soc_cutoff_aged, soc_cutoff_new);
unusable_soc = soc_cutoff_aged - soc_cutoff_new;
*actual_capacity = div64_s64(((int64_t)chip->nom_cap_uah)
* (1000 - unusable_soc), 1000);
if (fg_debug_mask & FG_AGING)
pr_info("nom cap = %d, actual cap = %d\n",
chip->nom_cap_uah, *actual_capacity);
return rc;
error_done:
pr_err("some register reads failed: %d\n", rc);
done:
fg_mem_release(chip);
return rc;
}
static void battery_age_work(struct work_struct *work)
{
struct fg_chip *chip = container_of(work,
struct fg_chip,
battery_age_work);
estimate_battery_age(chip, &chip->actual_cap_uah);
}
static enum power_supply_property fg_power_props[] = {
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_RAW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_NOW_RAW,
POWER_SUPPLY_PROP_CHARGE_NOW_ERROR,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_COOL_TEMP,
POWER_SUPPLY_PROP_WARM_TEMP,
POWER_SUPPLY_PROP_RESISTANCE,
POWER_SUPPLY_PROP_RESISTANCE_ID,
POWER_SUPPLY_PROP_BATTERY_TYPE,
POWER_SUPPLY_PROP_UPDATE_NOW,
POWER_SUPPLY_PROP_ESR_COUNT,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_PROFILE_STATUS,
POWER_SUPPLY_PROP_CYCLE_COUNT_ID,
};
static int fg_power_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct fg_chip *chip = container_of(psy, struct fg_chip, bms_psy);
bool vbatt_low_sts;
switch (psp) {
case POWER_SUPPLY_PROP_BATTERY_TYPE:
val->strval = chip->batt_type;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = get_prop_capacity(chip);
break;
case POWER_SUPPLY_PROP_CAPACITY_RAW:
val->intval = get_sram_prop_now(chip, FG_DATA_BATT_SOC);
break;
case POWER_SUPPLY_PROP_CHARGE_NOW_ERROR:
val->intval = get_sram_prop_now(chip, FG_DATA_VINT_ERR);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = get_sram_prop_now(chip, FG_DATA_CURRENT);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = get_sram_prop_now(chip, FG_DATA_VOLTAGE);
break;
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
val->intval = get_sram_prop_now(chip, FG_DATA_OCV);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = chip->batt_max_voltage_uv;
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
break;
case POWER_SUPPLY_PROP_COOL_TEMP:
val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_COLD);
break;
case POWER_SUPPLY_PROP_WARM_TEMP:
val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_HOT);
break;
case POWER_SUPPLY_PROP_RESISTANCE:
val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR);
break;
case POWER_SUPPLY_PROP_ESR_COUNT:
val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR_COUNT);
break;
case POWER_SUPPLY_PROP_CYCLE_COUNT:
val->intval = fg_get_cycle_count(chip);
break;
case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
val->intval = chip->cyc_ctr.id;
break;
case POWER_SUPPLY_PROP_RESISTANCE_ID:
val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID);
break;
case POWER_SUPPLY_PROP_UPDATE_NOW:
val->intval = 0;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
if (!fg_get_vbatt_status(chip, &vbatt_low_sts))
val->intval = (int)vbatt_low_sts;
else
val->intval = 1;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval = chip->nom_cap_uah;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
val->intval = chip->learning_data.learned_cc_uah;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
val->intval = chip->learning_data.cc_uah;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW_RAW:
val->intval = get_sram_prop_now(chip, FG_DATA_CC_CHARGE);
break;
case POWER_SUPPLY_PROP_PROFILE_STATUS:
val->intval = chip->profile_loaded;
break;
default:
return -EINVAL;
}
return 0;
}
static int correction_times[] = {
1470,
2940,
4410,
5880,
7350,
8820,
10290,
11760,
13230,
14700,
16170,
17640,
19110,
20580,
22050,
23520,
24990,
26460,
27930,
29400,
30870,
32340,
33810,
35280,
36750,
38220,
39690,
41160,
42630,
44100,
45570,
47040,
};
static int correction_factors[] = {
1000000,
1007874,
1015789,
1023745,
1031742,
1039780,
1047859,
1055979,
1064140,
1072342,
1080584,
1088868,
1097193,
1105558,
1113964,
1122411,
1130899,
1139427,
1147996,
1156606,
1165256,
1173947,
1182678,
1191450,
1200263,
1209115,
1218008,
1226942,
1235915,
1244929,
1253983,
1263076,
};
#define FG_CONVERSION_FACTOR (64198531LL)
static int iavg_3b_to_uah(u8 *buffer, int delta_ms)
{
int64_t val, i_filtered;
int i, correction_factor;
for (i = 0; i < ARRAY_SIZE(correction_times); i++) {
if (correction_times[i] > delta_ms)
break;
}
if (i >= ARRAY_SIZE(correction_times)) {
if (fg_debug_mask & FG_STATUS)
pr_info("fuel gauge took more than 32 cycles\n");
i = ARRAY_SIZE(correction_times) - 1;
}
correction_factor = correction_factors[i];
if (fg_debug_mask & FG_STATUS)
pr_info("delta_ms = %d, cycles = %d, correction = %d\n",
delta_ms, i, correction_factor);
val = buffer[2] << 16 | buffer[1] << 8 | buffer[0];
/* convert val from signed 24b to signed 64b */
i_filtered = (val << 40) >> 40;
val = i_filtered * correction_factor;
val = div64_s64(val + FG_CONVERSION_FACTOR / 2, FG_CONVERSION_FACTOR);
if (fg_debug_mask & FG_STATUS)
pr_info("i_filtered = 0x%llx/%lld, cc_uah = %lld\n",
i_filtered, i_filtered, val);
return val;
}
static bool fg_is_temperature_ok_for_learning(struct fg_chip *chip)
{
int batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
if (batt_temp > chip->learning_data.max_temp
|| batt_temp < chip->learning_data.min_temp) {
if (fg_debug_mask & FG_AGING)
pr_info("temp (%d) out of range [%d, %d], aborting\n",
batt_temp,
chip->learning_data.min_temp,
chip->learning_data.max_temp);
return false;
}
return true;
}
static void fg_cap_learning_stop(struct fg_chip *chip)
{
chip->learning_data.cc_uah = 0;
chip->learning_data.active = false;
}
#define I_FILTERED_REG 0x584
static void fg_cap_learning_work(struct work_struct *work)
{
struct fg_chip *chip = container_of(work,
struct fg_chip,
fg_cap_learning_work);
u8 i_filtered[3], data[3];
int rc, cc_uah, delta_ms;
ktime_t now_kt, delta_kt;
mutex_lock(&chip->learning_data.learning_lock);
if (!chip->learning_data.active)
goto fail;
if (!fg_is_temperature_ok_for_learning(chip)) {
fg_cap_learning_stop(chip);
goto fail;
}
fg_mem_lock(chip);
rc = fg_mem_read(chip, i_filtered, I_FILTERED_REG, 3, 0, 0);
if (rc) {
pr_err("Failed to read i_filtered: %d\n", rc);
fg_mem_release(chip);
goto fail;
}
memset(data, 0, 3);
rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0);
if (rc) {
pr_err("Failed to clear i_filtered: %d\n", rc);
fg_mem_release(chip);
goto fail;
}
fg_mem_release(chip);
now_kt = ktime_get_boottime();
delta_kt = ktime_sub(now_kt, chip->learning_data.time_stamp);
chip->learning_data.time_stamp = now_kt;
delta_ms = (int)div64_s64(ktime_to_ns(delta_kt), 1000000);
cc_uah = iavg_3b_to_uah(i_filtered, delta_ms);
chip->learning_data.cc_uah -= cc_uah;
if (fg_debug_mask & FG_AGING)
pr_info("total_cc_uah = %lld\n", chip->learning_data.cc_uah);
fail:
mutex_unlock(&chip->learning_data.learning_lock);
return;
}
#define FG_CAP_LEARNING_INTERVAL_NS 30000000000
static enum alarmtimer_restart fg_cap_learning_alarm_cb(struct alarm *alarm,
ktime_t now)
{
struct fg_chip *chip = container_of(alarm, struct fg_chip,
fg_cap_learning_alarm);
if (chip->learning_data.active) {
if (fg_debug_mask & FG_AGING)
pr_info("alarm fired\n");
schedule_work(&chip->fg_cap_learning_work);
alarm_forward_now(alarm,
ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS));
return ALARMTIMER_RESTART;
}
if (fg_debug_mask & FG_AGING)
pr_info("alarm misfired\n");
return ALARMTIMER_NORESTART;
}
#define FG_AGING_STORAGE_REG 0x5E4
#define ACTUAL_CAPACITY_REG 0x578
#define MAH_TO_SOC_CONV_REG 0x4A0
#define CC_SOC_COEFF_OFFSET 0
#define ACTUAL_CAPACITY_OFFSET 2
#define MAH_TO_SOC_CONV_CS_OFFSET 0
static void fg_cap_learning_load_data(struct fg_chip *chip)
{
int16_t cc_mah;
int64_t old_cap = chip->learning_data.learned_cc_uah;
int rc;
rc = fg_mem_read(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0);
if (rc) {
pr_err("Failed to load aged capacity: %d\n", rc);
} else {
chip->learning_data.learned_cc_uah = cc_mah * 1000;
if (fg_debug_mask & FG_AGING)
pr_info("learned capacity %lld-> %lld/%x uah\n",
old_cap,
chip->learning_data.learned_cc_uah,
cc_mah);
}
}
static void fg_cap_learning_save_data(struct fg_chip *chip)
{
int16_t cc_mah;
int64_t cc_to_soc_coeff, mah_to_soc;
int rc;
u8 data[2];
cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000);
rc = fg_mem_write(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0);
if (rc)
pr_err("Failed to store aged capacity: %d\n", rc);
else if (fg_debug_mask & FG_AGING)
pr_info("learned capacity %lld uah (%d/0x%x uah) saved to sram\n",
chip->learning_data.learned_cc_uah,
cc_mah, cc_mah);
if (chip->learning_data.feedback_on) {
rc = fg_mem_write(chip, (u8 *)&cc_mah, ACTUAL_CAPACITY_REG, 2,
ACTUAL_CAPACITY_OFFSET, 0);
if (rc)
pr_err("Failed to store actual capacity: %d\n", rc);
rc = fg_mem_read(chip, (u8 *)&data, MAH_TO_SOC_CONV_REG, 2,
MAH_TO_SOC_CONV_CS_OFFSET, 0);
if (rc) {
pr_err("Failed to read mah_to_soc_conv_cs: %d\n", rc);
} else {
mah_to_soc = data[1] << 8 | data[0];
mah_to_soc *= MICRO_UNIT;
cc_to_soc_coeff = div64_s64(mah_to_soc, cc_mah);
half_float_to_buffer(cc_to_soc_coeff, data);
rc = fg_mem_write(chip, (u8 *)data,
ACTUAL_CAPACITY_REG, 2,
CC_SOC_COEFF_OFFSET, 0);
if (rc)
pr_err("Failed to write cc_soc_coeff_offset: %d\n",
rc);
else if (fg_debug_mask & FG_AGING)
pr_info("new cc_soc_coeff %lld [%x %x] saved to sram\n",
cc_to_soc_coeff, data[0], data[1]);
}
}
}
static void fg_cap_learning_post_process(struct fg_chip *chip)
{
int64_t max_inc_val, min_dec_val, old_cap;
max_inc_val = chip->learning_data.learned_cc_uah
* (1000 + chip->learning_data.max_increment);
do_div(max_inc_val, 1000);
min_dec_val = chip->learning_data.learned_cc_uah
* (1000 - chip->learning_data.max_decrement);
do_div(min_dec_val, 1000);
old_cap = chip->learning_data.learned_cc_uah;
if (chip->learning_data.cc_uah > max_inc_val)
chip->learning_data.learned_cc_uah = max_inc_val;
else if (chip->learning_data.cc_uah < min_dec_val)
chip->learning_data.learned_cc_uah = min_dec_val;
else
chip->learning_data.learned_cc_uah =
chip->learning_data.cc_uah;
fg_cap_learning_save_data(chip);
if (fg_debug_mask & FG_AGING)
pr_info("final cc_uah = %lld, learned capacity %lld -> %lld uah\n",
chip->learning_data.cc_uah,
old_cap, chip->learning_data.learned_cc_uah);
}
static int get_vbat_est_diff(struct fg_chip *chip)
{
return abs(fg_data[FG_DATA_VOLTAGE].value
- fg_data[FG_DATA_CPRED_VOLTAGE].value);
}
#define CBITS_INPUT_FILTER_REG 0x4B4
#define IBATTF_TAU_MASK 0x38
#define IBATTF_TAU_99_S 0x30
static int fg_cap_learning_check(struct fg_chip *chip)
{
u8 data[3];
int rc = 0, battery_soc;
int vbat_est_diff, vbat_est_thr_uv;
mutex_lock(&chip->learning_data.learning_lock);
if (chip->status == POWER_SUPPLY_STATUS_CHARGING
&& !chip->learning_data.active
&& chip->batt_aging_mode == FG_AGING_CC) {
if (chip->learning_data.learned_cc_uah == 0) {
if (fg_debug_mask & FG_AGING)
pr_info("no capacity, aborting\n");
goto fail;
}
if (!fg_is_temperature_ok_for_learning(chip))
goto fail;
fg_mem_lock(chip);
if (!chip->learning_data.feedback_on) {
vbat_est_diff = get_vbat_est_diff(chip);
vbat_est_thr_uv = chip->learning_data.vbat_est_thr_uv;
if (vbat_est_diff >= vbat_est_thr_uv &&
vbat_est_thr_uv > 0) {
if (fg_debug_mask & FG_AGING)
pr_info("vbat_est_diff (%d) < threshold (%d)\n",
vbat_est_diff, vbat_est_thr_uv);
fg_mem_release(chip);
fg_cap_learning_stop(chip);
goto fail;
}
}
battery_soc = get_battery_soc_raw(chip);
if (fg_debug_mask & FG_AGING)
pr_info("checking battery soc (%d vs %d)\n",
battery_soc * 100 / FULL_PERCENT_3B,
chip->learning_data.max_start_soc);
/* check if the battery is low enough to start soc learning */
if (battery_soc * 100 / FULL_PERCENT_3B
> chip->learning_data.max_start_soc) {
if (fg_debug_mask & FG_AGING)
pr_info("battery soc too low (%d < %d), aborting\n",
battery_soc * 100 / FULL_PERCENT_3B,
chip->learning_data.max_start_soc);
fg_mem_release(chip);
fg_cap_learning_stop(chip);
goto fail;
}
/* set the coulomb counter to a percentage of the capacity */
chip->learning_data.cc_uah = div64_s64(
(chip->learning_data.learned_cc_uah * battery_soc),
FULL_PERCENT_3B);
rc = fg_mem_masked_write(chip, CBITS_INPUT_FILTER_REG,
IBATTF_TAU_MASK, IBATTF_TAU_99_S, 0);
if (rc) {
pr_err("Failed to write IF IBAT Tau: %d\n", rc);
fg_mem_release(chip);
fg_cap_learning_stop(chip);
goto fail;
}
/* clear the i_filtered register */
memset(data, 0, 3);
rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0);
if (rc) {
pr_err("Failed to clear i_filtered: %d\n", rc);
fg_mem_release(chip);
fg_cap_learning_stop(chip);
goto fail;
}
fg_mem_release(chip);
chip->learning_data.time_stamp = ktime_get_boottime();
chip->learning_data.active = true;
if (fg_debug_mask & FG_AGING)
pr_info("cap learning started, soc = %d cc_uah = %lld\n",
battery_soc * 100 / FULL_PERCENT_3B,
chip->learning_data.cc_uah);
rc = alarm_start_relative(&chip->fg_cap_learning_alarm,
ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS));
if (rc) {
pr_err("Failed to start alarm: %d\n", rc);
fg_cap_learning_stop(chip);
goto fail;
}
} else if (chip->status != POWER_SUPPLY_STATUS_CHARGING
&& chip->learning_data.active) {
if (fg_debug_mask & FG_AGING)
pr_info("capacity learning stopped\n");
alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
if (chip->status == POWER_SUPPLY_STATUS_FULL)
fg_cap_learning_post_process(chip);
fg_cap_learning_stop(chip);
}
fail:
mutex_unlock(&chip->learning_data.learning_lock);
return rc;
}
static bool is_usb_present(struct fg_chip *chip)
{
union power_supply_propval prop = {0,};
if (!chip->usb_psy)
chip->usb_psy = power_supply_get_by_name("usb");
if (chip->usb_psy)
chip->usb_psy->get_property(chip->usb_psy,
POWER_SUPPLY_PROP_PRESENT, &prop);
return prop.intval != 0;
}
static bool is_dc_present(struct fg_chip *chip)
{
union power_supply_propval prop = {0,};
if (!chip->dc_psy)
chip->dc_psy = power_supply_get_by_name("dc");
if (chip->dc_psy)
chip->dc_psy->get_property(chip->dc_psy,
POWER_SUPPLY_PROP_PRESENT, &prop);
return prop.intval != 0;
}
static bool is_input_present(struct fg_chip *chip)
{
return is_usb_present(chip) || is_dc_present(chip);
}
static void status_change_work(struct work_struct *work)
{
struct fg_chip *chip = container_of(work,
struct fg_chip,
status_change_work);
unsigned long current_time = 0;
int capacity = get_prop_capacity(chip);
if (chip->status == POWER_SUPPLY_STATUS_FULL) {
if (capacity >= 99 && chip->hold_soc_while_full) {
if (fg_debug_mask & FG_STATUS)
pr_info("holding soc at 100\n");
chip->charge_full = true;
} else if (fg_debug_mask & FG_STATUS) {
pr_info("terminated charging at %d/0x%02x\n",
capacity, get_monotonic_soc_raw(chip));
}
}
if (chip->status == POWER_SUPPLY_STATUS_FULL ||
chip->status == POWER_SUPPLY_STATUS_CHARGING) {
if (!chip->vbat_low_irq_enabled) {
enable_irq(chip->batt_irq[VBATT_LOW].irq);
enable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
chip->vbat_low_irq_enabled = true;
}
if (capacity == 100)
fg_configure_soc(chip);
} else if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
if (chip->vbat_low_irq_enabled) {
disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
chip->vbat_low_irq_enabled = false;
}
}
fg_cap_learning_check(chip);
schedule_work(&chip->update_esr_work);
if (chip->prev_status != chip->status && chip->last_sram_update_time) {
get_current_time(&current_time);
/*
* When charging status changes, update SRAM parameters if it
* was not updated before 5 seconds from now.
*/
if (chip->last_sram_update_time + 5 < current_time) {
cancel_delayed_work(&chip->update_sram_data);
schedule_delayed_work(&chip->update_sram_data,
msecs_to_jiffies(0));
}
if (chip->cyc_ctr.en)
schedule_work(&chip->cycle_count_work);
}
}
static int fg_power_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct fg_chip *chip = container_of(psy, struct fg_chip, bms_psy);
int rc = 0, unused;
switch (psp) {
case POWER_SUPPLY_PROP_COOL_TEMP:
rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_COLD, val->intval);
break;
case POWER_SUPPLY_PROP_WARM_TEMP:
rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_HOT, val->intval);
break;
case POWER_SUPPLY_PROP_UPDATE_NOW:
if (val->intval)
update_sram_data(chip, &unused);
break;
case POWER_SUPPLY_PROP_STATUS:
chip->prev_status = chip->status;
chip->status = val->intval;
schedule_work(&chip->status_change_work);
break;
case POWER_SUPPLY_PROP_HEALTH:
chip->health = val->intval;
if (chip->health == POWER_SUPPLY_HEALTH_GOOD) {
fg_stay_awake(&chip->resume_soc_wakeup_source);
schedule_work(&chip->set_resume_soc_work);
}
break;
case POWER_SUPPLY_PROP_CHARGE_DONE:
chip->charge_done = val->intval;
if (!chip->resume_soc_lowered) {
fg_stay_awake(&chip->resume_soc_wakeup_source);
schedule_work(&chip->set_resume_soc_work);
}
break;
case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
if ((val->intval > 0) && (val->intval <= BUCKET_COUNT)) {
chip->cyc_ctr.id = val->intval;
} else {
pr_err("rejecting invalid cycle_count_id = %d\n",
val->intval);
rc = -EINVAL;
}
break;
default:
return -EINVAL;
};
return rc;
};
static int fg_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
switch (psp) {
case POWER_SUPPLY_PROP_COOL_TEMP:
case POWER_SUPPLY_PROP_WARM_TEMP:
case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
return 1;
default:
break;
}
return 0;
}
#define SRAM_DUMP_START 0x400
#define SRAM_DUMP_LEN 0x200
static void dump_sram(struct work_struct *work)
{
int i, rc;
u8 *buffer, rt_sts;
char str[16];
struct fg_chip *chip = container_of(work,
struct fg_chip,
dump_sram);
buffer = devm_kzalloc(chip->dev, SRAM_DUMP_LEN, GFP_KERNEL);
if (buffer == NULL) {
pr_err("Can't allocate buffer\n");
return;
}
rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->soc_base), 1);
if (rc)
pr_err("spmi read failed: addr=%03X, rc=%d\n",
INT_RT_STS(chip->soc_base), rc);
else
pr_info("soc rt_sts: 0x%x\n", rt_sts);
rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->batt_base), 1);
if (rc)
pr_err("spmi read failed: addr=%03X, rc=%d\n",
INT_RT_STS(chip->batt_base), rc);
else
pr_info("batt rt_sts: 0x%x\n", rt_sts);
rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->mem_base), 1);
if (rc)
pr_err("spmi read failed: addr=%03X, rc=%d\n",
INT_RT_STS(chip->mem_base), rc);
else
pr_info("memif rt_sts: 0x%x\n", rt_sts);
rc = fg_mem_read(chip, buffer, SRAM_DUMP_START, SRAM_DUMP_LEN, 0, 0);
if (rc) {
pr_err("dump failed: rc = %d\n", rc);
return;
}
for (i = 0; i < SRAM_DUMP_LEN; i += 4) {
str[0] = '\0';
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buffer + i, 4);
pr_info("%03X %s\n", SRAM_DUMP_START + i, str);
}
devm_kfree(chip->dev, buffer);
}
#define MAXRSCHANGE_REG 0x434
#define ESR_VALUE_OFFSET 1
#define ESR_STRICT_VALUE 0x4120391F391F3019
#define ESR_DEFAULT_VALUE 0x58CD4A6761C34A67
static void update_esr_value(struct work_struct *work)
{
union power_supply_propval prop = {0, };
u64 esr_value;
int rc = 0;
struct fg_chip *chip = container_of(work,
struct fg_chip,
update_esr_work);
if (!chip->batt_psy && chip->batt_psy_name)
chip->batt_psy = power_supply_get_by_name(chip->batt_psy_name);
if (chip->batt_psy)
chip->batt_psy->get_property(chip->batt_psy,
POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
else
return;
if (!chip->esr_strict_filter) {
if ((prop.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER &&
chip->status == POWER_SUPPLY_STATUS_CHARGING) ||
(chip->status == POWER_SUPPLY_STATUS_FULL)) {
esr_value = ESR_STRICT_VALUE;
rc = fg_mem_write(chip, (u8 *)&esr_value,
MAXRSCHANGE_REG, 8,
ESR_VALUE_OFFSET, 0);
if (rc)
pr_err("failed to write strict ESR value rc=%d\n",
rc);
else
chip->esr_strict_filter = true;
}
} else if ((prop.intval != POWER_SUPPLY_CHARGE_TYPE_TAPER &&
chip->status == POWER_SUPPLY_STATUS_CHARGING) ||
(chip->status == POWER_SUPPLY_STATUS_DISCHARGING)) {
esr_value = ESR_DEFAULT_VALUE;
rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8,
ESR_VALUE_OFFSET, 0);
if (rc)
pr_err("failed to write default ESR value rc=%d\n", rc);
else
chip->esr_strict_filter = false;
}
}
#define BATT_MISSING_STS BIT(6)
static bool is_battery_missing(struct fg_chip *chip)
{
int rc;
u8 fg_batt_sts;
rc = fg_read(chip, &fg_batt_sts,
INT_RT_STS(chip->batt_base), 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n",
INT_RT_STS(chip->batt_base), rc);
return false;
}
return (fg_batt_sts & BATT_MISSING_STS) ? true : false;
}
static irqreturn_t fg_vbatt_low_handler(int irq, void *_chip)
{
struct fg_chip *chip = _chip;
int rc;
bool vbatt_low_sts;
if (fg_debug_mask & FG_IRQS)
pr_info("vbatt-low triggered\n");
if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
rc = fg_get_vbatt_status(chip, &vbatt_low_sts);
if (rc) {
pr_err("error in reading vbatt_status, rc:%d\n", rc);
goto out;
}
if (!vbatt_low_sts && chip->vbat_low_irq_enabled) {
if (fg_debug_mask & FG_IRQS)
pr_info("disabling vbatt_low irq\n");
disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
chip->vbat_low_irq_enabled = false;
}
}
if (chip->power_supply_registered)
power_supply_changed(&chip->bms_psy);
out:
return IRQ_HANDLED;
}
static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip)
{
struct fg_chip *chip = _chip;
bool batt_missing = is_battery_missing(chip);
if (batt_missing) {
chip->battery_missing = true;
chip->profile_loaded = false;
chip->batt_type = missing_batt_type;
mutex_lock(&chip->cyc_ctr.lock);
if (fg_debug_mask & FG_IRQS)
pr_info("battery missing, clearing cycle counters\n");
clear_cycle_counter(chip);
mutex_unlock(&chip->cyc_ctr.lock);
} else {
if (!chip->use_otp_profile) {
INIT_COMPLETION(chip->batt_id_avail);
schedule_work(&chip->batt_profile_init);
cancel_delayed_work(&chip->update_sram_data);
schedule_delayed_work(
&chip->update_sram_data,
msecs_to_jiffies(0));
} else {
chip->battery_missing = false;
}
}
if (fg_debug_mask & FG_IRQS)
pr_info("batt-missing triggered: %s\n",
batt_missing ? "missing" : "present");
if (chip->power_supply_registered)
power_supply_changed(&chip->bms_psy);
return IRQ_HANDLED;
}
static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip)
{
struct fg_chip *chip = _chip;
u8 mem_if_sts;
int rc;
rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
if (rc) {
pr_err("failed to read mem status rc=%d\n", rc);
return IRQ_HANDLED;
}
if (fg_check_sram_access(chip)) {
if ((fg_debug_mask & FG_IRQS)
& (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
pr_info("sram access granted\n");
INIT_COMPLETION(chip->sram_access_revoked);
complete_all(&chip->sram_access_granted);
} else {
if ((fg_debug_mask & FG_IRQS)
& (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
pr_info("sram access revoked\n");
complete_all(&chip->sram_access_revoked);
}
if (!rc && (fg_debug_mask & FG_IRQS)
& (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
pr_info("mem_if sts 0x%02x\n", mem_if_sts);
return IRQ_HANDLED;
}
static irqreturn_t fg_soc_irq_handler(int irq, void *_chip)
{
struct fg_chip *chip = _chip;
u8 soc_rt_sts;
int rc;
rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n",
INT_RT_STS(chip->soc_base), rc);
}
if (fg_debug_mask & FG_IRQS)
pr_info("triggered 0x%x\n", soc_rt_sts);
schedule_work(&chip->battery_age_work);
if (chip->power_supply_registered)
power_supply_changed(&chip->bms_psy);
if (chip->rslow_comp.chg_rs_to_rslow > 0 &&
chip->rslow_comp.chg_rslow_comp_c1 > 0 &&
chip->rslow_comp.chg_rslow_comp_c2 > 0)
schedule_work(&chip->rslow_comp_work);
if (chip->cyc_ctr.en)
schedule_work(&chip->cycle_count_work);
schedule_work(&chip->update_esr_work);
if (chip->charge_full)
schedule_work(&chip->charge_full_work);
return IRQ_HANDLED;
}
#define FG_EMPTY_DEBOUNCE_MS 1500
static irqreturn_t fg_empty_soc_irq_handler(int irq, void *_chip)
{
struct fg_chip *chip = _chip;
u8 soc_rt_sts;
int rc;
rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n",
INT_RT_STS(chip->soc_base), rc);
goto done;
}
if (fg_debug_mask & FG_IRQS)
pr_info("triggered 0x%x\n", soc_rt_sts);
if (fg_is_batt_empty(chip)) {
fg_stay_awake(&chip->empty_check_wakeup_source);
schedule_delayed_work(&chip->check_empty_work,
msecs_to_jiffies(FG_EMPTY_DEBOUNCE_MS));
} else {
chip->soc_empty = false;
}
done:
return IRQ_HANDLED;
}
static irqreturn_t fg_first_soc_irq_handler(int irq, void *_chip)
{
struct fg_chip *chip = _chip;
if (fg_debug_mask & FG_IRQS)
pr_info("triggered\n");
if (fg_est_dump)
schedule_work(&chip->dump_sram);
if (chip->power_supply_registered)
power_supply_changed(&chip->bms_psy);
return IRQ_HANDLED;
}
static void fg_external_power_changed(struct power_supply *psy)
{
struct fg_chip *chip = container_of(psy, struct fg_chip, bms_psy);
if (is_input_present(chip) && chip->rslow_comp.active &&
chip->rslow_comp.chg_rs_to_rslow > 0 &&