Loading Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt +6 −0 Original line number Diff line number Diff line Loading @@ -216,6 +216,12 @@ First Level Node - FG Gen3 device Definition: Battery temperature delta interrupt threshold. Possible values are: 2, 4, 6 and 10. Unit is in Kelvin. - qcom,hold-soc-while-full: Usage: optional Value type: <bool> Definition: A boolean property that when defined holds SOC at 100% when the battery is full. ========================================================== Second Level Nodes - Peripherals managed by FG Gen3 driver ========================================================== Loading drivers/power/qcom-charger/fg-core.h +9 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/string_helpers.h> Loading @@ -38,6 +39,7 @@ pr_debug(fmt, ##__VA_ARGS__); \ } while (0) /* Awake votable reasons */ #define SRAM_READ "fg_sram_read" #define SRAM_WRITE "fg_sram_write" #define PROFILE_LOAD "fg_profile_load" Loading Loading @@ -173,6 +175,8 @@ struct fg_alg_flag { /* DT parameters for FG device */ struct fg_dt_props { bool force_load_profile; bool hold_soc_while_full; int cutoff_volt_mv; int empty_volt_mv; int vbatt_low_thr_mv; Loading @@ -185,7 +189,6 @@ struct fg_dt_props { int esr_timer_charging; int esr_timer_awake; int esr_timer_asleep; bool force_load_profile; int cl_start_soc; int cl_max_temp; int cl_min_temp; Loading Loading @@ -240,6 +243,8 @@ struct fg_chip { struct dentry *dfs_root; struct power_supply *fg_psy; struct power_supply *batt_psy; struct power_supply *usb_psy; struct power_supply *dc_psy; struct iio_channel *batt_id_chan; struct fg_memif *sram; struct fg_irq_info *irqs; Loading Loading @@ -268,6 +273,8 @@ struct fg_chip { bool profile_loaded; bool battery_missing; bool fg_restarting; bool charge_full; bool recharge_soc_adjusted; struct completion soc_update; struct completion soc_ready; struct delayed_work profile_load_work; Loading Loading @@ -321,4 +328,5 @@ extern int fg_debugfs_create(struct fg_chip *chip); extern void fill_string(char *str, size_t str_len, u8 *buf, int buf_len); extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos); extern s64 fg_float_decode(u16 val); extern bool is_input_present(struct fg_chip *chip); #endif drivers/power/qcom-charger/fg-util.c +38 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,43 @@ static struct fg_dbgfs dbgfs_data = { }, }; static bool is_usb_present(struct fg_chip *chip) { union power_supply_propval pval = {0, }; if (!chip->usb_psy) chip->usb_psy = power_supply_get_by_name("usb"); if (chip->usb_psy) power_supply_get_property(chip->usb_psy, POWER_SUPPLY_PROP_PRESENT, &pval); else return false; return pval.intval != 0; } static bool is_dc_present(struct fg_chip *chip) { union power_supply_propval pval = {0, }; if (!chip->dc_psy) chip->dc_psy = power_supply_get_by_name("dc"); if (chip->dc_psy) power_supply_get_property(chip->dc_psy, POWER_SUPPLY_PROP_PRESENT, &pval); else return false; return pval.intval != 0; } bool is_input_present(struct fg_chip *chip) { return is_usb_present(chip) || is_dc_present(chip); } #define EXPONENT_SHIFT 11 #define EXPONENT_OFFSET -9 #define MANTISSA_SIGN_BIT 10 Loading Loading @@ -98,6 +135,7 @@ int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset, * This interrupt need to be enabled only when it is * required. It will be kept disabled other times. */ reinit_completion(&chip->soc_update); enable_irq(chip->irqs[SOC_UPDATE_IRQ].irq); atomic_access = true; } else { Loading drivers/power/qcom-charger/qpnp-fg-gen3.c +182 −36 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ #include <linux/of_platform.h> #include <linux/of_batterydata.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/iio/consumer.h> #include <linux/qpnp/qpnp-revid.h> #include "fg-core.h" Loading Loading @@ -65,6 +64,8 @@ #define PROFILE_INTEGRITY_OFFSET 3 #define BATT_SOC_WORD 91 #define BATT_SOC_OFFSET 0 #define FULL_SOC_WORD 93 #define FULL_SOC_OFFSET 2 #define MONOTONIC_SOC_WORD 94 #define MONOTONIC_SOC_OFFSET 2 #define CC_SOC_WORD 95 Loading Loading @@ -106,8 +107,6 @@ static int fg_decode_value_16b(struct fg_sram_param *sp, enum fg_sram_param_id id, int val); static int fg_decode_default(struct fg_sram_param *sp, enum fg_sram_param_id id, int val); static int fg_decode_batt_soc(struct fg_sram_param *sp, enum fg_sram_param_id id, int val); static int fg_decode_cc_soc(struct fg_sram_param *sp, enum fg_sram_param_id id, int value); static void fg_encode_voltage(struct fg_sram_param *sp, Loading @@ -132,7 +131,7 @@ static void fg_encode_default(struct fg_sram_param *sp, static struct fg_sram_param pmicobalt_v1_sram_params[] = { PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL, fg_decode_batt_soc), fg_decode_default), PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141, 1000, 0, NULL, fg_decode_voltage_15b), PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL, Loading Loading @@ -178,7 +177,7 @@ static struct fg_sram_param pmicobalt_v1_sram_params[] = { static struct fg_sram_param pmicobalt_v2_sram_params[] = { PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL, fg_decode_batt_soc), fg_decode_default), PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141, 1000, 0, NULL, fg_decode_voltage_15b), PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL, Loading Loading @@ -335,19 +334,11 @@ static int fg_decode_value_16b(struct fg_sram_param *sp, return sp[id].value; } static int fg_decode_batt_soc(struct fg_sram_param *sp, enum fg_sram_param_id id, int value) { sp[id].value = (u32)value >> 24; pr_debug("id: %d raw value: %x decoded value: %x\n", id, value, sp[id].value); return sp[id].value; } static int fg_decode_default(struct fg_sram_param *sp, enum fg_sram_param_id id, int value) { return value; sp[id].value = value; return sp[id].value; } static int fg_decode(struct fg_sram_param *sp, enum fg_sram_param_id id, Loading Loading @@ -645,6 +636,11 @@ static int fg_get_prop_capacity(struct fg_chip *chip, int *val) { int rc, msoc; if (chip->charge_full) { *val = FULL_CAPACITY; return 0; } rc = fg_get_msoc_raw(chip, &msoc); if (rc < 0) return rc; Loading Loading @@ -1059,7 +1055,7 @@ static int fg_cap_learning_done(struct fg_chip *chip) cc_soc_sw = CC_SOC_30BIT; rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word, chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw, chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_DEFAULT); chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC); if (rc < 0) { pr_err("Error in writing cc_soc_sw, rc=%d\n", rc); goto out; Loading Loading @@ -1092,6 +1088,9 @@ static void fg_cap_learning_update(struct fg_chip *chip) goto out; } /* We need only the most significant byte here */ batt_soc = (u32)batt_soc >> 24; fg_dbg(chip, FG_CAP_LEARN, "Chg_status: %d cl_active: %d batt_soc: %d\n", chip->status, chip->cl.active, batt_soc); Loading @@ -1103,8 +1102,7 @@ static void fg_cap_learning_update(struct fg_chip *chip) } } else { if (chip->status == POWER_SUPPLY_STATUS_FULL && chip->charge_done) { if (chip->charge_done) { rc = fg_cap_learning_done(chip); if (rc < 0) pr_err("Error in completing capacity learning, rc=%d\n", Loading @@ -1126,19 +1124,157 @@ out: mutex_unlock(&chip->cl.lock); } static int fg_charge_full_update(struct fg_chip *chip) { union power_supply_propval prop = {0, }; int rc, msoc, bsoc, recharge_soc; u8 full_soc[2] = {0xFF, 0xFF}; if (!chip->dt.hold_soc_while_full) return 0; if (!is_charger_available(chip)) return 0; rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_HEALTH, &prop); if (rc < 0) { pr_err("Error in getting battery health, rc=%d\n", rc); return rc; } chip->health = prop.intval; recharge_soc = chip->dt.recharge_soc_thr; recharge_soc = DIV_ROUND_CLOSEST(recharge_soc * FULL_SOC_RAW, FULL_CAPACITY); rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &bsoc); if (rc < 0) { pr_err("Error in getting BATT_SOC, rc=%d\n", rc); return rc; } /* We need 2 most significant bytes here */ bsoc = (u32)bsoc >> 16; rc = fg_get_prop_capacity(chip, &msoc); if (rc < 0) { pr_err("Error in getting capacity, rc=%d\n", rc); return rc; } fg_dbg(chip, FG_STATUS, "msoc: %d health: %d status: %d\n", msoc, chip->health, chip->status); if (chip->charge_done) { if (msoc >= 99 && chip->health == POWER_SUPPLY_HEALTH_GOOD) chip->charge_full = true; else fg_dbg(chip, FG_STATUS, "Terminated charging @ SOC%d\n", msoc); } else if ((bsoc >> 8) <= recharge_soc) { fg_dbg(chip, FG_STATUS, "bsoc: %d recharge_soc: %d\n", bsoc >> 8, recharge_soc); chip->charge_full = false; } if (!chip->charge_full) return 0; /* * During JEITA conditions, charge_full can happen early. FULL_SOC * and MONOTONIC_SOC needs to be updated to reflect the same. Write * battery SOC to FULL_SOC and write a full value to MONOTONIC_SOC. */ rc = fg_sram_write(chip, FULL_SOC_WORD, FULL_SOC_OFFSET, (u8 *)&bsoc, 2, FG_IMA_ATOMIC); if (rc < 0) { pr_err("failed to write full_soc rc=%d\n", rc); return rc; } rc = fg_sram_write(chip, MONOTONIC_SOC_WORD, MONOTONIC_SOC_OFFSET, full_soc, 2, FG_IMA_ATOMIC); if (rc < 0) { pr_err("failed to write monotonic_soc rc=%d\n", rc); return rc; } fg_dbg(chip, FG_STATUS, "Set charge_full to true @ soc %d\n", msoc); return 0; } static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc) { u8 buf[4]; int rc; fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, recharge_soc, buf); rc = fg_sram_write(chip, chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word, chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_byte, buf, chip->sp[FG_SRAM_RECHARGE_SOC_THR].len, FG_IMA_DEFAULT); if (rc < 0) { pr_err("Error in writing recharge_soc_thr, rc=%d\n", rc); return rc; } return 0; } static int fg_adjust_recharge_soc(struct fg_chip *chip) { int rc, msoc, recharge_soc, new_recharge_soc = 0; recharge_soc = chip->dt.recharge_soc_thr; /* * If the input is present and charging had been terminated, adjust * the recharge SOC threshold based on the monotonic SOC at which * the charge termination had happened. */ if (is_input_present(chip) && !chip->recharge_soc_adjusted && chip->charge_done) { /* Get raw monotonic SOC for calculation */ rc = fg_get_msoc_raw(chip, &msoc); if (rc < 0) { pr_err("Error in getting msoc, rc=%d\n", rc); return rc; } msoc = DIV_ROUND_CLOSEST(msoc * FULL_CAPACITY, FULL_SOC_RAW); /* Adjust the recharge_soc threshold */ new_recharge_soc = msoc - (FULL_CAPACITY - recharge_soc); } else if (chip->recharge_soc_adjusted && (!is_input_present(chip) || chip->health == POWER_SUPPLY_HEALTH_GOOD)) { /* Restore the default value */ new_recharge_soc = recharge_soc; } if (new_recharge_soc > 0 && new_recharge_soc < FULL_CAPACITY) { rc = fg_set_recharge_soc(chip, new_recharge_soc); if (rc) { pr_err("Couldn't set resume SOC for FG, rc=%d\n", rc); return rc; } chip->recharge_soc_adjusted = (new_recharge_soc != recharge_soc); fg_dbg(chip, FG_STATUS, "resume soc set to %d\n", new_recharge_soc); } return 0; } static void status_change_work(struct work_struct *work) { struct fg_chip *chip = container_of(work, struct fg_chip, status_change_work); union power_supply_propval prop = {0, }; int prev_status, rc; int rc; if (!is_charger_available(chip)) { fg_dbg(chip, FG_STATUS, "Charger not available?!\n"); goto out; } prev_status = chip->status; rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, &prop); if (rc < 0) { Loading @@ -1155,13 +1291,20 @@ static void status_change_work(struct work_struct *work) } chip->charge_done = prop.intval; fg_dbg(chip, FG_POWER_SUPPLY, "prev_status: %d curr_status:%d charge_done: %d\n", prev_status, chip->status, chip->charge_done); if (prev_status != chip->status) { fg_dbg(chip, FG_POWER_SUPPLY, "curr_status:%d charge_done: %d\n", chip->status, chip->charge_done); if (chip->cyc_ctr.en) schedule_work(&chip->cycle_count_work); fg_cap_learning_update(chip); } rc = fg_charge_full_update(chip); if (rc < 0) pr_err("Error in charge_full_update, rc=%d\n", rc); rc = fg_adjust_recharge_soc(chip); if (rc < 0) pr_err("Error in adjusting recharge_soc, rc=%d\n", rc); out: pm_relax(chip->dev); Loading Loading @@ -1247,6 +1390,9 @@ static void cycle_count_work(struct work_struct *work) goto out; } /* We need only the most significant byte here */ batt_soc = (u32)batt_soc >> 24; if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { /* Find out which bucket the SOC falls in */ bucket = batt_soc / BUCKET_SOC_PCT; Loading Loading @@ -1787,16 +1933,9 @@ static int fg_hw_init(struct fg_chip *chip) } if (chip->dt.recharge_soc_thr > 0 && chip->dt.recharge_soc_thr < 100) { fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, chip->dt.recharge_soc_thr, buf); rc = fg_sram_write(chip, chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word, chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_byte, buf, chip->sp[FG_SRAM_RECHARGE_SOC_THR].len, FG_IMA_DEFAULT); rc = fg_set_recharge_soc(chip, chip->dt.recharge_soc_thr); if (rc < 0) { pr_err("Error in writing recharge_soc_thr, rc=%d\n", rc); pr_err("Error in setting recharge_soc, rc=%d\n", rc); return rc; } } Loading Loading @@ -1982,6 +2121,7 @@ static irqreturn_t fg_soc_update_irq_handler(int irq, void *data) static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data) { struct fg_chip *chip = data; int rc; if (chip->cyc_ctr.en) schedule_work(&chip->cycle_count_work); Loading @@ -1994,6 +2134,10 @@ static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data) if (chip->cl.active) fg_cap_learning_update(chip); rc = fg_charge_full_update(chip); if (rc < 0) pr_err("Error in charge_full_update, rc=%d\n", rc); return IRQ_HANDLED; } Loading Loading @@ -2416,6 +2560,8 @@ static int fg_parse_dt(struct fg_chip *chip) else if (temp > BTEMP_DELTA_LOW && temp <= BTEMP_DELTA_HIGH) chip->dt.batt_temp_delta = temp; chip->dt.hold_soc_while_full = of_property_read_bool(node, "qcom,hold-soc-while-full"); return 0; } Loading Loading
Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt +6 −0 Original line number Diff line number Diff line Loading @@ -216,6 +216,12 @@ First Level Node - FG Gen3 device Definition: Battery temperature delta interrupt threshold. Possible values are: 2, 4, 6 and 10. Unit is in Kelvin. - qcom,hold-soc-while-full: Usage: optional Value type: <bool> Definition: A boolean property that when defined holds SOC at 100% when the battery is full. ========================================================== Second Level Nodes - Peripherals managed by FG Gen3 driver ========================================================== Loading
drivers/power/qcom-charger/fg-core.h +9 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/string_helpers.h> Loading @@ -38,6 +39,7 @@ pr_debug(fmt, ##__VA_ARGS__); \ } while (0) /* Awake votable reasons */ #define SRAM_READ "fg_sram_read" #define SRAM_WRITE "fg_sram_write" #define PROFILE_LOAD "fg_profile_load" Loading Loading @@ -173,6 +175,8 @@ struct fg_alg_flag { /* DT parameters for FG device */ struct fg_dt_props { bool force_load_profile; bool hold_soc_while_full; int cutoff_volt_mv; int empty_volt_mv; int vbatt_low_thr_mv; Loading @@ -185,7 +189,6 @@ struct fg_dt_props { int esr_timer_charging; int esr_timer_awake; int esr_timer_asleep; bool force_load_profile; int cl_start_soc; int cl_max_temp; int cl_min_temp; Loading Loading @@ -240,6 +243,8 @@ struct fg_chip { struct dentry *dfs_root; struct power_supply *fg_psy; struct power_supply *batt_psy; struct power_supply *usb_psy; struct power_supply *dc_psy; struct iio_channel *batt_id_chan; struct fg_memif *sram; struct fg_irq_info *irqs; Loading Loading @@ -268,6 +273,8 @@ struct fg_chip { bool profile_loaded; bool battery_missing; bool fg_restarting; bool charge_full; bool recharge_soc_adjusted; struct completion soc_update; struct completion soc_ready; struct delayed_work profile_load_work; Loading Loading @@ -321,4 +328,5 @@ extern int fg_debugfs_create(struct fg_chip *chip); extern void fill_string(char *str, size_t str_len, u8 *buf, int buf_len); extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos); extern s64 fg_float_decode(u16 val); extern bool is_input_present(struct fg_chip *chip); #endif
drivers/power/qcom-charger/fg-util.c +38 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,43 @@ static struct fg_dbgfs dbgfs_data = { }, }; static bool is_usb_present(struct fg_chip *chip) { union power_supply_propval pval = {0, }; if (!chip->usb_psy) chip->usb_psy = power_supply_get_by_name("usb"); if (chip->usb_psy) power_supply_get_property(chip->usb_psy, POWER_SUPPLY_PROP_PRESENT, &pval); else return false; return pval.intval != 0; } static bool is_dc_present(struct fg_chip *chip) { union power_supply_propval pval = {0, }; if (!chip->dc_psy) chip->dc_psy = power_supply_get_by_name("dc"); if (chip->dc_psy) power_supply_get_property(chip->dc_psy, POWER_SUPPLY_PROP_PRESENT, &pval); else return false; return pval.intval != 0; } bool is_input_present(struct fg_chip *chip) { return is_usb_present(chip) || is_dc_present(chip); } #define EXPONENT_SHIFT 11 #define EXPONENT_OFFSET -9 #define MANTISSA_SIGN_BIT 10 Loading Loading @@ -98,6 +135,7 @@ int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset, * This interrupt need to be enabled only when it is * required. It will be kept disabled other times. */ reinit_completion(&chip->soc_update); enable_irq(chip->irqs[SOC_UPDATE_IRQ].irq); atomic_access = true; } else { Loading
drivers/power/qcom-charger/qpnp-fg-gen3.c +182 −36 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ #include <linux/of_platform.h> #include <linux/of_batterydata.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/iio/consumer.h> #include <linux/qpnp/qpnp-revid.h> #include "fg-core.h" Loading Loading @@ -65,6 +64,8 @@ #define PROFILE_INTEGRITY_OFFSET 3 #define BATT_SOC_WORD 91 #define BATT_SOC_OFFSET 0 #define FULL_SOC_WORD 93 #define FULL_SOC_OFFSET 2 #define MONOTONIC_SOC_WORD 94 #define MONOTONIC_SOC_OFFSET 2 #define CC_SOC_WORD 95 Loading Loading @@ -106,8 +107,6 @@ static int fg_decode_value_16b(struct fg_sram_param *sp, enum fg_sram_param_id id, int val); static int fg_decode_default(struct fg_sram_param *sp, enum fg_sram_param_id id, int val); static int fg_decode_batt_soc(struct fg_sram_param *sp, enum fg_sram_param_id id, int val); static int fg_decode_cc_soc(struct fg_sram_param *sp, enum fg_sram_param_id id, int value); static void fg_encode_voltage(struct fg_sram_param *sp, Loading @@ -132,7 +131,7 @@ static void fg_encode_default(struct fg_sram_param *sp, static struct fg_sram_param pmicobalt_v1_sram_params[] = { PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL, fg_decode_batt_soc), fg_decode_default), PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141, 1000, 0, NULL, fg_decode_voltage_15b), PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL, Loading Loading @@ -178,7 +177,7 @@ static struct fg_sram_param pmicobalt_v1_sram_params[] = { static struct fg_sram_param pmicobalt_v2_sram_params[] = { PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL, fg_decode_batt_soc), fg_decode_default), PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141, 1000, 0, NULL, fg_decode_voltage_15b), PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL, Loading Loading @@ -335,19 +334,11 @@ static int fg_decode_value_16b(struct fg_sram_param *sp, return sp[id].value; } static int fg_decode_batt_soc(struct fg_sram_param *sp, enum fg_sram_param_id id, int value) { sp[id].value = (u32)value >> 24; pr_debug("id: %d raw value: %x decoded value: %x\n", id, value, sp[id].value); return sp[id].value; } static int fg_decode_default(struct fg_sram_param *sp, enum fg_sram_param_id id, int value) { return value; sp[id].value = value; return sp[id].value; } static int fg_decode(struct fg_sram_param *sp, enum fg_sram_param_id id, Loading Loading @@ -645,6 +636,11 @@ static int fg_get_prop_capacity(struct fg_chip *chip, int *val) { int rc, msoc; if (chip->charge_full) { *val = FULL_CAPACITY; return 0; } rc = fg_get_msoc_raw(chip, &msoc); if (rc < 0) return rc; Loading Loading @@ -1059,7 +1055,7 @@ static int fg_cap_learning_done(struct fg_chip *chip) cc_soc_sw = CC_SOC_30BIT; rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word, chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw, chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_DEFAULT); chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC); if (rc < 0) { pr_err("Error in writing cc_soc_sw, rc=%d\n", rc); goto out; Loading Loading @@ -1092,6 +1088,9 @@ static void fg_cap_learning_update(struct fg_chip *chip) goto out; } /* We need only the most significant byte here */ batt_soc = (u32)batt_soc >> 24; fg_dbg(chip, FG_CAP_LEARN, "Chg_status: %d cl_active: %d batt_soc: %d\n", chip->status, chip->cl.active, batt_soc); Loading @@ -1103,8 +1102,7 @@ static void fg_cap_learning_update(struct fg_chip *chip) } } else { if (chip->status == POWER_SUPPLY_STATUS_FULL && chip->charge_done) { if (chip->charge_done) { rc = fg_cap_learning_done(chip); if (rc < 0) pr_err("Error in completing capacity learning, rc=%d\n", Loading @@ -1126,19 +1124,157 @@ out: mutex_unlock(&chip->cl.lock); } static int fg_charge_full_update(struct fg_chip *chip) { union power_supply_propval prop = {0, }; int rc, msoc, bsoc, recharge_soc; u8 full_soc[2] = {0xFF, 0xFF}; if (!chip->dt.hold_soc_while_full) return 0; if (!is_charger_available(chip)) return 0; rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_HEALTH, &prop); if (rc < 0) { pr_err("Error in getting battery health, rc=%d\n", rc); return rc; } chip->health = prop.intval; recharge_soc = chip->dt.recharge_soc_thr; recharge_soc = DIV_ROUND_CLOSEST(recharge_soc * FULL_SOC_RAW, FULL_CAPACITY); rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &bsoc); if (rc < 0) { pr_err("Error in getting BATT_SOC, rc=%d\n", rc); return rc; } /* We need 2 most significant bytes here */ bsoc = (u32)bsoc >> 16; rc = fg_get_prop_capacity(chip, &msoc); if (rc < 0) { pr_err("Error in getting capacity, rc=%d\n", rc); return rc; } fg_dbg(chip, FG_STATUS, "msoc: %d health: %d status: %d\n", msoc, chip->health, chip->status); if (chip->charge_done) { if (msoc >= 99 && chip->health == POWER_SUPPLY_HEALTH_GOOD) chip->charge_full = true; else fg_dbg(chip, FG_STATUS, "Terminated charging @ SOC%d\n", msoc); } else if ((bsoc >> 8) <= recharge_soc) { fg_dbg(chip, FG_STATUS, "bsoc: %d recharge_soc: %d\n", bsoc >> 8, recharge_soc); chip->charge_full = false; } if (!chip->charge_full) return 0; /* * During JEITA conditions, charge_full can happen early. FULL_SOC * and MONOTONIC_SOC needs to be updated to reflect the same. Write * battery SOC to FULL_SOC and write a full value to MONOTONIC_SOC. */ rc = fg_sram_write(chip, FULL_SOC_WORD, FULL_SOC_OFFSET, (u8 *)&bsoc, 2, FG_IMA_ATOMIC); if (rc < 0) { pr_err("failed to write full_soc rc=%d\n", rc); return rc; } rc = fg_sram_write(chip, MONOTONIC_SOC_WORD, MONOTONIC_SOC_OFFSET, full_soc, 2, FG_IMA_ATOMIC); if (rc < 0) { pr_err("failed to write monotonic_soc rc=%d\n", rc); return rc; } fg_dbg(chip, FG_STATUS, "Set charge_full to true @ soc %d\n", msoc); return 0; } static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc) { u8 buf[4]; int rc; fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, recharge_soc, buf); rc = fg_sram_write(chip, chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word, chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_byte, buf, chip->sp[FG_SRAM_RECHARGE_SOC_THR].len, FG_IMA_DEFAULT); if (rc < 0) { pr_err("Error in writing recharge_soc_thr, rc=%d\n", rc); return rc; } return 0; } static int fg_adjust_recharge_soc(struct fg_chip *chip) { int rc, msoc, recharge_soc, new_recharge_soc = 0; recharge_soc = chip->dt.recharge_soc_thr; /* * If the input is present and charging had been terminated, adjust * the recharge SOC threshold based on the monotonic SOC at which * the charge termination had happened. */ if (is_input_present(chip) && !chip->recharge_soc_adjusted && chip->charge_done) { /* Get raw monotonic SOC for calculation */ rc = fg_get_msoc_raw(chip, &msoc); if (rc < 0) { pr_err("Error in getting msoc, rc=%d\n", rc); return rc; } msoc = DIV_ROUND_CLOSEST(msoc * FULL_CAPACITY, FULL_SOC_RAW); /* Adjust the recharge_soc threshold */ new_recharge_soc = msoc - (FULL_CAPACITY - recharge_soc); } else if (chip->recharge_soc_adjusted && (!is_input_present(chip) || chip->health == POWER_SUPPLY_HEALTH_GOOD)) { /* Restore the default value */ new_recharge_soc = recharge_soc; } if (new_recharge_soc > 0 && new_recharge_soc < FULL_CAPACITY) { rc = fg_set_recharge_soc(chip, new_recharge_soc); if (rc) { pr_err("Couldn't set resume SOC for FG, rc=%d\n", rc); return rc; } chip->recharge_soc_adjusted = (new_recharge_soc != recharge_soc); fg_dbg(chip, FG_STATUS, "resume soc set to %d\n", new_recharge_soc); } return 0; } static void status_change_work(struct work_struct *work) { struct fg_chip *chip = container_of(work, struct fg_chip, status_change_work); union power_supply_propval prop = {0, }; int prev_status, rc; int rc; if (!is_charger_available(chip)) { fg_dbg(chip, FG_STATUS, "Charger not available?!\n"); goto out; } prev_status = chip->status; rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, &prop); if (rc < 0) { Loading @@ -1155,13 +1291,20 @@ static void status_change_work(struct work_struct *work) } chip->charge_done = prop.intval; fg_dbg(chip, FG_POWER_SUPPLY, "prev_status: %d curr_status:%d charge_done: %d\n", prev_status, chip->status, chip->charge_done); if (prev_status != chip->status) { fg_dbg(chip, FG_POWER_SUPPLY, "curr_status:%d charge_done: %d\n", chip->status, chip->charge_done); if (chip->cyc_ctr.en) schedule_work(&chip->cycle_count_work); fg_cap_learning_update(chip); } rc = fg_charge_full_update(chip); if (rc < 0) pr_err("Error in charge_full_update, rc=%d\n", rc); rc = fg_adjust_recharge_soc(chip); if (rc < 0) pr_err("Error in adjusting recharge_soc, rc=%d\n", rc); out: pm_relax(chip->dev); Loading Loading @@ -1247,6 +1390,9 @@ static void cycle_count_work(struct work_struct *work) goto out; } /* We need only the most significant byte here */ batt_soc = (u32)batt_soc >> 24; if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { /* Find out which bucket the SOC falls in */ bucket = batt_soc / BUCKET_SOC_PCT; Loading Loading @@ -1787,16 +1933,9 @@ static int fg_hw_init(struct fg_chip *chip) } if (chip->dt.recharge_soc_thr > 0 && chip->dt.recharge_soc_thr < 100) { fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, chip->dt.recharge_soc_thr, buf); rc = fg_sram_write(chip, chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word, chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_byte, buf, chip->sp[FG_SRAM_RECHARGE_SOC_THR].len, FG_IMA_DEFAULT); rc = fg_set_recharge_soc(chip, chip->dt.recharge_soc_thr); if (rc < 0) { pr_err("Error in writing recharge_soc_thr, rc=%d\n", rc); pr_err("Error in setting recharge_soc, rc=%d\n", rc); return rc; } } Loading Loading @@ -1982,6 +2121,7 @@ static irqreturn_t fg_soc_update_irq_handler(int irq, void *data) static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data) { struct fg_chip *chip = data; int rc; if (chip->cyc_ctr.en) schedule_work(&chip->cycle_count_work); Loading @@ -1994,6 +2134,10 @@ static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data) if (chip->cl.active) fg_cap_learning_update(chip); rc = fg_charge_full_update(chip); if (rc < 0) pr_err("Error in charge_full_update, rc=%d\n", rc); return IRQ_HANDLED; } Loading Loading @@ -2416,6 +2560,8 @@ static int fg_parse_dt(struct fg_chip *chip) else if (temp > BTEMP_DELTA_LOW && temp <= BTEMP_DELTA_HIGH) chip->dt.batt_temp_delta = temp; chip->dt.hold_soc_while_full = of_property_read_bool(node, "qcom,hold-soc-while-full"); return 0; } Loading