Loading Documentation/devicetree/bindings/power/smb1360-charger-fg.txt +26 −5 Original line number Diff line number Diff line Loading @@ -117,11 +117,28 @@ Optional Properties: - qcom,config-hard-thresholds This property indicates if cold and hot thresholds need be modified. If it's not defined, default temp thresholds will be used. If this defined, it's required to specify the "qcom,hot-bat-decidegc" and "qcom,cold-bat-decidegc" values. - qcom,hot-bat-decidegc Hot battery temperature in decidegC. This property must be out of range [0, 55]. - qcom,cold-bat-decidegc Cold battery temperature in decidegC. This property must be out of range [0, 55]. "qcom,cold-bat-decidegc" values. If this defined, the serial properties (qcom,otp-hard-jeita-config, qcom,otp-hot-bat-decidegc, qcom,otp-cold-bat-decidegc) mustn't be defined. - qcom,hot-bat-decidegc Hot battery temperature in decidegC. This property should only be defined when "qcom,config-hard-thresholds" defined, and the value must be out of range [0, 55]. - qcom,cold-bat-decidegc Cold battery temperature in decidegC. This property should only be defined when "qcom,config-hard-thresholds" defined, and the value must be out of range [0, 55]. - qcom,otp-hard-jeita-config This property indicates if cold and hot thresholds need be modified. If it's not defined, default temp thresholds [0, 55] will be used. If this defined, it's required to specify the "qcom,otp-hot-bat-decidegc" and "qcom,otp-cold-bat-decidegc" values. If this is defined, the serial properties (qcom,config-hard-thresholds, qcom,hot-bat-decidegc, qcom,cold-bat-decidegc) mustn't be defined. - qcom,otp-hot-bat-decidegc Hot battery temperature in decidegC. This property could only be defined when qcom,config-otp-hard-jeita exist. - qcom,otp-cold-bat-decidegc Cold battery temperature in decidegC. This property could only be defined when qcom,config-otp-hard-jeita exist. - qcom,otp-hard-jeita-hysteresis This property contains two intergers to define the temperature hysteresis (in decidegC unit) for hard cold and hard hot. This property could only be defined when qcom,config-otp-hard-jeita exist. - qcom,soft-jeita-supported This property indicates whether soft jeita supported. - qcom,warm-bat-decidegc Warm battery temperature in decidegC. After hitting this threshold, "qcom,warm-bat-ma" defines maximum charging current and Loading Loading @@ -211,6 +228,10 @@ Example: qcom,soft-jeita-supported; qcom,warm-bat-decidegc = <450>; qcom,cool-bat-decidegc = <100>; qcom,otp-hard-jeita-config; qcom,otp-hot-bat-decidegc = <550>; qcom,otp-cold-bat-decidegc = <0>; qcom,otp-hard-jeita-hysteresis = <0, 50>; qcom,warm-bat-mv = <4100>; qcom,cool-bat-mv = <4100>; qcom,warm-bat-ma = <750>; Loading drivers/power/qpnp-linear-charger.c +217 −67 Original line number Diff line number Diff line Loading @@ -40,8 +40,15 @@ /* USB CHARGER PATH peripheral register offsets */ #define USB_IN_VALID_MASK BIT(1) #define CHG_GONE_BIT BIT(2) #define USB_SUSP_REG 0x47 #define USB_SUSPEND_BIT BIT(0) #define USB_COMP_OVR1_REG 0xEA #define USBIN_LLIMIT_OK_MASK LBC_MASK(1, 0) #define USBIN_LLIMIT_OK_NO_OVERRIDE 0x00 #define USBIN_LLIMIT_OK_OVERRIDE_1 0x03 #define USB_OVP_TST5_REG 0xE7 #define CHG_GONE_OK_EN_BIT BIT(2) /* CHARGER peripheral register offset */ #define CHG_OPTION_REG 0x08 Loading Loading @@ -71,6 +78,8 @@ #define CHG_PERPH_RESET_CTRL3_REG 0xDA #define CHG_COMP_OVR1 0xEE #define CHG_VBAT_DET_OVR_MASK LBC_MASK(1, 0) #define CHG_TEST_LOOP_REG 0xE5 #define VIN_MIN_LOOP_DISABLE_BIT BIT(0) #define OVERRIDE_0 0x2 #define OVERRIDE_NONE 0x0 Loading Loading @@ -147,6 +156,7 @@ enum { CURRENT = BIT(2), SOC = BIT(3), PARALLEL = BIT(4), COLLAPSE = BIT(5), }; enum bpd_type { Loading Loading @@ -272,6 +282,7 @@ struct vddtrim_map vddtrim_map[] = { * @cfg_charger_detect_eoc: charger can detect end of charging * @cfg_disable_vbatdet_based_recharge: keep VBATDET comparator overriden to * low and VBATDET irq disabled. * @cfg_collapsible_chgr_support: support collapsible charger * @cfg_chgr_led_support: support charger led work. * @cfg_safe_current: battery safety current setting * @cfg_hot_batt_p: hot battery threshold setting Loading Loading @@ -324,7 +335,7 @@ struct qpnp_lbc_chip { bool fastchg_on; bool cfg_use_external_charger; bool cfg_chgr_led_support; bool cfg_bms_controlled_charging; bool non_collapsible_chgr_detected; unsigned int cfg_warm_bat_chg_ma; unsigned int cfg_cool_bat_chg_ma; unsigned int cfg_safe_voltage_mv; Loading @@ -333,6 +344,7 @@ struct qpnp_lbc_chip { unsigned int cfg_charger_detect_eoc; unsigned int cfg_disable_vbatdet_based_recharge; unsigned int cfg_batt_weak_voltage_uv; unsigned int cfg_collapsible_chgr_support; unsigned int cfg_warm_bat_mv; unsigned int cfg_cool_bat_mv; unsigned int cfg_hot_batt_p; Loading @@ -355,6 +367,7 @@ struct qpnp_lbc_chip { int usb_psy_ma; int delta_vddmax_uv; int init_trim_uv; struct delayed_work collapsible_detection_work; /* parallel-chg params */ int parallel_charging_enabled; Loading Loading @@ -598,9 +611,10 @@ static u8 qpnp_lbc_get_trim_val(struct qpnp_lbc_chip *chip) return vddtrim_map[i + 1].trim_val; } } i = 0; break; case 1: for (i = TRIM_CENTER; i <= 7; i++) { for (i = TRIM_CENTER; i < ARRAY_SIZE(vddtrim_map); i++) { if (vddtrim_map[i].trim_uv < chip->delta_vddmax_uv) { delta_uv = AVG(vddtrim_map[i].trim_uv, vddtrim_map[i - 1].trim_uv); Loading @@ -610,6 +624,7 @@ static u8 qpnp_lbc_get_trim_val(struct qpnp_lbc_chip *chip) return vddtrim_map[i].trim_val; } } i = ARRAY_SIZE(vddtrim_map) - 1; break; } Loading @@ -634,6 +649,24 @@ static int qpnp_lbc_is_usb_chg_plugged_in(struct qpnp_lbc_chip *chip) return (usbin_valid_rt_sts & USB_IN_VALID_MASK) ? 1 : 0; } static int qpnp_lbc_is_chg_gone(struct qpnp_lbc_chip *chip) { u8 rt_sts; int rc; rc = qpnp_lbc_read(chip, chip->usb_chgpth_base + INT_RT_STS_REG, &rt_sts, 1); if (rc) { pr_err("spmi read failed: addr=0x%04x, rc=%d\n", chip->usb_chgpth_base + INT_RT_STS_REG, rc); return rc; } pr_debug("rt_sts 0x%x\n", rt_sts); return (rt_sts & CHG_GONE_BIT) ? 1 : 0; } static int qpnp_lbc_charger_enable(struct qpnp_lbc_chip *chip, int reason, int enable) { Loading Loading @@ -700,10 +733,9 @@ static int qpnp_lbc_bat_if_configure_btc(struct qpnp_lbc_chip *chip) mask |= BTC_COLD_MASK; } if (!chip->cfg_btc_disabled) { mask |= BTC_COMP_EN_MASK; if (!chip->cfg_btc_disabled) btc_cfg |= BTC_COMP_EN_MASK; } pr_debug("BTC configuration mask=%x\n", btc_cfg); Loading @@ -716,6 +748,75 @@ static int qpnp_lbc_bat_if_configure_btc(struct qpnp_lbc_chip *chip) return rc; } static int qpnp_chg_collapsible_chgr_config(struct qpnp_lbc_chip *chip, bool enable) { u8 reg_val; int rc; pr_debug("Configure for %scollapsible charger\n", enable ? "" : "non-"); /* * The flow to enable/disable the collapsible charger configuration: * Enable: Override USBIN_LLIMIT_OK --> * Disable VIN_MIN comparator --> * Enable CHG_GONE comparator * Disable: Enable VIN_MIN comparator --> * Enable USBIN_LLIMIT_OK --> * Disable CHG_GONE comparator */ if (enable) { /* Override USBIN_LLIMIT_OK */ reg_val = USBIN_LLIMIT_OK_OVERRIDE_1; rc = __qpnp_lbc_secure_masked_write(chip->spmi, chip->usb_chgpth_base, USB_COMP_OVR1_REG, USBIN_LLIMIT_OK_MASK, reg_val); if (rc) { pr_err("Failed to override USB_LLIMIT_OK rc = %d\n", rc); return rc; } } /* Configure VIN_MIN comparator */ rc = __qpnp_lbc_secure_masked_write(chip->spmi, chip->chgr_base, CHG_TEST_LOOP_REG, VIN_MIN_LOOP_DISABLE_BIT, enable ? VIN_MIN_LOOP_DISABLE_BIT : 0); if (rc) { pr_err("Failed to %s VIN_MIN comparator rc = %d\n", enable ? "disable" : "enable", rc); return rc; } if (!enable) { /* Enable USBIN_LLIMIT_OK */ reg_val = USBIN_LLIMIT_OK_NO_OVERRIDE; rc = __qpnp_lbc_secure_masked_write(chip->spmi, chip->usb_chgpth_base, USB_COMP_OVR1_REG, USBIN_LLIMIT_OK_MASK, reg_val); if (rc) { pr_err("Failed to override USB_LLIMIT_OK rc = %d\n", rc); return rc; } } /* Configure CHG_GONE comparator */ reg_val = enable ? CHG_GONE_OK_EN_BIT : 0; rc = __qpnp_lbc_secure_masked_write(chip->spmi, chip->usb_chgpth_base, USB_OVP_TST5_REG, CHG_GONE_OK_EN_BIT, reg_val); if (rc) { pr_err("Failed to write CHG_GONE comparator rc = %d\n", rc); return rc; } return 0; } #define QPNP_LBC_VBATWEAK_MIN_UV 3000000 #define QPNP_LBC_VBATWEAK_MAX_UV 3581250 #define QPNP_LBC_VBATWEAK_STEP_UV 18750 Loading Loading @@ -1218,7 +1319,7 @@ static int get_prop_current_now(struct qpnp_lbc_chip *chip) static int get_prop_capacity(struct qpnp_lbc_chip *chip) { union power_supply_propval ret = {0,}; int soc, battery_status, charger_in; int soc; if (chip->fake_battery_soc >= 0) return chip->fake_battery_soc; Loading @@ -1229,33 +1330,6 @@ static int get_prop_capacity(struct qpnp_lbc_chip *chip) if (chip->bms_psy) { chip->bms_psy->get_property(chip->bms_psy, POWER_SUPPLY_PROP_CAPACITY, &ret); mutex_lock(&chip->chg_enable_lock); if (chip->chg_done) chip->bms_psy->get_property(chip->bms_psy, POWER_SUPPLY_PROP_CAPACITY, &ret); battery_status = get_prop_batt_status(chip); charger_in = qpnp_lbc_is_usb_chg_plugged_in(chip); /* reset chg_done flag if capacity not 100% */ if (ret.intval < 100 && chip->chg_done) { chip->chg_done = false; power_supply_changed(&chip->batt_psy); } if (battery_status != POWER_SUPPLY_STATUS_CHARGING && charger_in && !chip->cfg_charging_disabled && chip->cfg_soc_resume_limit && ret.intval <= chip->cfg_soc_resume_limit && !chip->cfg_bms_controlled_charging) { pr_debug("resuming charging at %d%% soc\n", ret.intval); if (!chip->cfg_disable_vbatdet_based_recharge) qpnp_lbc_vbatdet_override(chip, OVERRIDE_0); qpnp_lbc_charger_enable(chip, SOC, 1); } mutex_unlock(&chip->chg_enable_lock); soc = ret.intval; if (soc == 0) { if (!qpnp_lbc_is_usb_chg_plugged_in(chip)) Loading Loading @@ -1523,10 +1597,10 @@ static int qpnp_batt_power_set_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_STATUS: mutex_lock(&chip->chg_enable_lock); if (val->intval == POWER_SUPPLY_STATUS_FULL && !chip->cfg_float_charge) { switch (val->intval) { case POWER_SUPPLY_STATUS_FULL: if (chip->cfg_float_charge) break; /* Disable charging */ rc = qpnp_lbc_charger_enable(chip, SOC, 0); if (rc) Loading @@ -1551,17 +1625,13 @@ static int qpnp_batt_power_set_property(struct power_supply *psy, qpnp_lbc_enable_irq(chip, &chip->irqs[CHG_VBAT_DET_LO]); } } if (chip->cfg_bms_controlled_charging) { switch (val->intval) { break; case POWER_SUPPLY_STATUS_CHARGING: chip->chg_done = false; pr_debug("resuming charging by bms\n"); if (!chip->cfg_disable_vbatdet_based_recharge) qpnp_lbc_vbatdet_override(chip, OVERRIDE_0); qpnp_lbc_vbatdet_override(chip, OVERRIDE_0); qpnp_lbc_charger_enable(chip, SOC, 1); break; case POWER_SUPPLY_STATUS_DISCHARGING: Loading @@ -1572,8 +1642,6 @@ static int qpnp_batt_power_set_property(struct power_supply *psy, default: break; } } mutex_unlock(&chip->chg_enable_lock); break; case POWER_SUPPLY_PROP_COOL_TEMP: Loading Loading @@ -2105,7 +2173,6 @@ static int show_lbc_config(struct seq_file *m, void *data) "cfg_use_fake_battery\t=\t%d\n" "cfg_use_external_charger\t=\t%d\n" "cfg_chgr_led_support\t=\t%d\n" "cfg_bms_controlled_charging\t=\t%d\n" "cfg_warm_bat_chg_ma\t=\t%d\n" "cfg_cool_bat_chg_ma\t=\t%d\n" "cfg_safe_voltage_mv\t=\t%d\n" Loading @@ -2113,6 +2180,7 @@ static int show_lbc_config(struct seq_file *m, void *data) "cfg_min_voltage_mv\t=\t%d\n" "cfg_charger_detect_eoc\t=\t%d\n" "cfg_disable_vbatdet_based_recharge\t=\t%d\n" "cfg_collapsible_chgr_support\t=\t%d\n" "cfg_batt_weak_voltage_uv\t=\t%d\n" "cfg_warm_bat_mv\t=\t%d\n" "cfg_cool_bat_mv\t=\t%d\n" Loading @@ -2131,7 +2199,6 @@ static int show_lbc_config(struct seq_file *m, void *data) chip->cfg_use_fake_battery, chip->cfg_use_external_charger, chip->cfg_chgr_led_support, chip->cfg_bms_controlled_charging, chip->cfg_warm_bat_chg_ma, chip->cfg_cool_bat_chg_ma, chip->cfg_safe_voltage_mv, Loading @@ -2139,6 +2206,7 @@ static int show_lbc_config(struct seq_file *m, void *data) chip->cfg_min_voltage_mv, chip->cfg_charger_detect_eoc, chip->cfg_disable_vbatdet_based_recharge, chip->cfg_collapsible_chgr_support, chip->cfg_batt_weak_voltage_uv, chip->cfg_warm_bat_mv, chip->cfg_cool_bat_mv, Loading Loading @@ -2284,9 +2352,10 @@ static int qpnp_charger_read_dt_props(struct qpnp_lbc_chip *chip) of_property_read_bool(chip->spmi->dev.of_node, "qcom,chgr-led-support"); chip->cfg_bms_controlled_charging = /* Get the collapsible charger support property */ chip->cfg_collapsible_chgr_support = of_property_read_bool(chip->spmi->dev.of_node, "qcom,bms-controlled-charging"); "qcom,collapsible-chgr-support"); /* Disable charging when faking battery values */ if (chip->cfg_use_fake_battery) Loading Loading @@ -2343,17 +2412,50 @@ static int qpnp_charger_read_dt_props(struct qpnp_lbc_chip *chip) chip->cfg_charging_disabled, chip->cfg_use_fake_battery, chip->cfg_float_charge); pr_debug("charger-detect-eoc=%d, disable-vbatdet-based-recharge=%d, chgr-led-support=%d, bms-controlled-charging=%d\n", pr_debug("charger-detect-eoc=%d, disable-vbatdet-based-recharge=%d, chgr-led-support=%d\n", chip->cfg_charger_detect_eoc, chip->cfg_disable_vbatdet_based_recharge, chip->cfg_chgr_led_support, chip->cfg_bms_controlled_charging); pr_debug("use-external-charger=%d, thermal_levels=%d\n", chip->cfg_chgr_led_support); pr_debug("collapsible-chg-support=%d, use-external-charger=%d, thermal_levels=%d\n", chip->cfg_collapsible_chgr_support, chip->cfg_use_external_charger, chip->cfg_thermal_levels); return rc; } #define CHG_REMOVAL_DETECT_DLY_MS 300 static irqreturn_t qpnp_lbc_chg_gone_irq_handler(int irq, void *_chip) { struct qpnp_lbc_chip *chip = _chip; int chg_gone; if (chip->cfg_collapsible_chgr_support) { chg_gone = qpnp_lbc_is_chg_gone(chip); pr_debug("chg-gone triggered, rt_sts: %d\n", chg_gone); if (chg_gone) { /* * Disable charger to prevent fastchg irq storming * if a non-collapsible charger is being used. */ pr_debug("disable charging for non-collapsbile charger\n"); qpnp_lbc_charger_enable(chip, COLLAPSE, 0); qpnp_lbc_disable_irq(chip, &chip->irqs[USBIN_VALID]); qpnp_lbc_disable_irq(chip, &chip->irqs[USB_CHG_GONE]); qpnp_chg_collapsible_chgr_config(chip, 0); /* * Check after a delay if the charger is still * inserted. It decides if a non-collapsible * charger is being used, or charger has been * removed. */ schedule_delayed_work(&chip->collapsible_detection_work, msecs_to_jiffies(CHG_REMOVAL_DETECT_DLY_MS)); } } return IRQ_HANDLED; } static irqreturn_t qpnp_lbc_usbin_valid_irq_handler(int irq, void *_chip) { struct qpnp_lbc_chip *chip = _chip; Loading @@ -2372,6 +2474,11 @@ static irqreturn_t qpnp_lbc_usbin_valid_irq_handler(int irq, void *_chip) qpnp_lbc_set_appropriate_current(chip); spin_unlock_irqrestore(&chip->ibat_change_lock, flags); if (chip->cfg_collapsible_chgr_support) chip->non_collapsible_chgr_detected = false; if (chip->supported_feature_flag & VDD_TRIM_SUPPORTED) alarm_try_to_cancel(&chip->vddtrim_alarm); } else { /* * Override VBAT_DET comparator to start charging Loading @@ -2380,6 +2487,16 @@ static irqreturn_t qpnp_lbc_usbin_valid_irq_handler(int irq, void *_chip) if (!chip->cfg_disable_vbatdet_based_recharge) qpnp_lbc_vbatdet_override(chip, OVERRIDE_0); /* * If collapsible charger supported, enable chgr_gone * irq, and configure for collapsible charger. */ if (chip->cfg_collapsible_chgr_support && !chip->non_collapsible_chgr_detected) { qpnp_lbc_enable_irq(chip, &chip->irqs[USB_CHG_GONE]); qpnp_chg_collapsible_chgr_config(chip, 1); } /* * Enable SOC based charging to make sure * charging gets enabled on USB insertion Loading Loading @@ -2685,6 +2802,9 @@ static int qpnp_lbc_request_irqs(struct qpnp_lbc_chip *chip) SPMI_REQUEST_IRQ(chip, USBIN_VALID, rc, usbin_valid, 0, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 1); SPMI_REQUEST_IRQ(chip, USB_CHG_GONE, rc, chg_gone, 0, IRQF_TRIGGER_RISING, 1); SPMI_REQUEST_IRQ(chip, USB_OVER_TEMP, rc, usb_overtemp, 0, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 0); Loading Loading @@ -2720,6 +2840,8 @@ static int qpnp_lbc_get_irqs(struct qpnp_lbc_chip *chip, u8 subtype, USBIN_VALID, usbin-valid); SPMI_GET_IRQ_RESOURCE(chip, rc, spmi_resource, USB_OVER_TEMP, usb-over-temp); SPMI_GET_IRQ_RESOURCE(chip, rc, spmi_resource, USB_CHG_GONE, chg-gone); break; }; Loading @@ -2735,9 +2857,34 @@ static void determine_initial_status(struct qpnp_lbc_chip *chip) * Set USB psy online to avoid userspace from shutting down if battery * capacity is at zero and no chargers online. */ if (chip->usb_present) if (chip->usb_present) { if (chip->cfg_collapsible_chgr_support && !chip->non_collapsible_chgr_detected) { qpnp_lbc_enable_irq(chip, &chip->irqs[USB_CHG_GONE]); qpnp_chg_collapsible_chgr_config(chip, 1); } power_supply_set_online(chip->usb_psy, 1); } } static void qpnp_lbc_collapsible_detection_work(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); struct qpnp_lbc_chip *chip = container_of(dwork, struct qpnp_lbc_chip, collapsible_detection_work); if (qpnp_lbc_is_usb_chg_plugged_in(chip)) { chip->non_collapsible_chgr_detected = true; pr_debug("Non-collapsible charger detected\n"); } else { chip->non_collapsible_chgr_detected = false; pr_debug("Charger removal detected\n"); } qpnp_lbc_charger_enable(chip, COLLAPSE, 1); qpnp_lbc_enable_irq(chip, &chip->irqs[USBIN_VALID]); } #define IBAT_TRIM -300 static void qpnp_lbc_vddtrim_work_fn(struct work_struct *work) Loading Loading @@ -3041,6 +3188,8 @@ static int qpnp_lbc_main_probe(struct spmi_device *spmi) spin_lock_init(&chip->irq_lock); INIT_WORK(&chip->vddtrim_work, qpnp_lbc_vddtrim_work_fn); alarm_init(&chip->vddtrim_alarm, ALARM_REALTIME, vddtrim_callback); INIT_DELAYED_WORK(&chip->collapsible_detection_work, qpnp_lbc_collapsible_detection_work); /* Get all device-tree properties */ rc = qpnp_charger_read_dt_props(chip); Loading Loading @@ -3226,6 +3375,7 @@ static int qpnp_lbc_remove(struct spmi_device *spmi) alarm_cancel(&chip->vddtrim_alarm); cancel_work_sync(&chip->vddtrim_work); } cancel_delayed_work_sync(&chip->collapsible_detection_work); debugfs_remove_recursive(chip->debug_root); if (chip->bat_if_base) power_supply_unregister(&chip->batt_psy); Loading drivers/power/qpnp-vm-bms.c +59 −23 Original line number Diff line number Diff line Loading @@ -35,7 +35,7 @@ #include <linux/qpnp/qpnp-adc.h> #include <linux/of_batterydata.h> #include <linux/batterydata-interface.h> #include <linux/qpnp-revid.h> #include <linux/qpnp/qpnp-revid.h> #include <uapi/linux/vm_bms.h> #define _BMS_MASK(BITS, POS) \ Loading Loading @@ -244,6 +244,7 @@ struct qpnp_bms_chip { u16 charge_cycles; unsigned int start_soc; unsigned int end_soc; unsigned int chg_start_soc; struct bms_battery_data *batt_data; struct bms_dt_cfg dt; Loading Loading @@ -962,7 +963,7 @@ static int lookup_soc_ocv(struct qpnp_bms_chip *chip, int ocv_uv, int batt_temp) if (chip->batt_data->ibat_acc_lut) { /* Apply ACC logic only if we discharging */ if (!is_battery_charging(chip) && chip->current_now > 0) { if (chip->current_now > 0) { /* * IBAT averaging is disabled at low temp. Loading Loading @@ -1607,7 +1608,9 @@ static int report_vm_bms_soc(struct qpnp_bms_chip *chip) * avoid overflows when charging continues for extended periods */ if (charging && chip->last_soc != -EINVAL) { if (chip->charge_start_tm_sec == 0) { if (chip->charge_start_tm_sec == 0 || (chip->catch_up_time_sec == 0 && (abs(soc - chip->last_soc) >= MIN_SOC_UUC))) { /* * calculating soc for the first time * after start of chg. Initialize catchup time Loading @@ -1619,17 +1622,25 @@ static int report_vm_bms_soc(struct qpnp_bms_chip *chip) else chip->catch_up_time_sec = SOC_CATCHUP_SEC_MAX; chip->chg_start_soc = chip->last_soc; if (chip->catch_up_time_sec < 0) chip->catch_up_time_sec = 0; chip->charge_start_tm_sec = last_change_sec; pr_debug("chg_start_soc=%d charge_start_tm_sec=%d catch_up_time_sec=%d\n", chip->chg_start_soc, chip->charge_start_tm_sec, chip->catch_up_time_sec); } charge_time_sec = min(SOC_CATCHUP_SEC_MAX, (int)last_change_sec - chip->charge_start_tm_sec); /* end catchup if calculated soc and last soc are same */ if (chip->last_soc == soc) if (chip->last_soc == soc) { chip->catch_up_time_sec = 0; chip->chg_start_soc = chip->last_soc; } } if (chip->last_soc != -EINVAL) { Loading @@ -1647,7 +1658,7 @@ static int report_vm_bms_soc(struct qpnp_bms_chip *chip) else if (chip->last_soc < soc && soc != 100) soc = scale_soc_while_chg(chip, charge_time_sec, chip->catch_up_time_sec, soc, chip->last_soc); soc, chip->chg_start_soc); /* * if the battery is close to cutoff or if the batt_temp Loading Loading @@ -1963,6 +1974,11 @@ static void calculate_reported_soc(struct qpnp_bms_chip *chip) { union power_supply_propval ret = {0,}; if (chip->last_soc < 0) { pr_debug("last_soc is not ready, return\n"); return; } if (chip->reported_soc > chip->last_soc) { /*send DISCHARGING status if the reported_soc drops from 100 */ if (chip->reported_soc == 100) { Loading Loading @@ -2022,13 +2038,26 @@ static int clamp_soc_based_on_voltage(struct qpnp_bms_chip *chip, int soc) } } static void battery_voltage_check(struct qpnp_bms_chip *chip) { int rc, vbat_uv = 0; rc = get_battery_voltage(chip, &vbat_uv); if (rc < 0) { pr_err("Failed to read battery-voltage rc=%d\n", rc); } else { very_low_voltage_check(chip, vbat_uv); cv_voltage_check(chip, vbat_uv); } } #define UI_SOC_CATCHUP_TIME (60) static void monitor_soc_work(struct work_struct *work) { struct qpnp_bms_chip *chip = container_of(work, struct qpnp_bms_chip, monitor_soc_work.work); int rc, vbat_uv = 0, new_soc = 0, batt_temp; int rc, new_soc = 0, batt_temp; bms_stay_awake(&chip->vbms_soc_wake_source); Loading @@ -2044,13 +2073,7 @@ static void monitor_soc_work(struct work_struct *work) chip->last_soc = -EINVAL; new_soc = 100; } else { rc = get_battery_voltage(chip, &vbat_uv); if (rc < 0) { pr_err("Failed to read battery-voltage rc=%d\n", rc); } else { very_low_voltage_check(chip, vbat_uv); cv_voltage_check(chip, vbat_uv); } battery_voltage_check(chip); if (chip->dt.cfg_use_voltage_soc) { calculate_soc_from_voltage(chip); Loading @@ -2075,6 +2098,11 @@ static void monitor_soc_work(struct work_struct *work) pr_debug("SOC changed! new_soc=%d prev_soc=%d\n", new_soc, chip->calculated_soc); chip->calculated_soc = new_soc; /* * To recalculate the catch-up time, clear it * when SOC changes. */ chip->catch_up_time_sec = 0; if (chip->calculated_soc == 100) /* update last_soc immediately */ Loading Loading @@ -2180,6 +2208,7 @@ static enum power_supply_property bms_power_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_RESISTANCE, POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE, POWER_SUPPLY_PROP_RESISTANCE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_VOLTAGE_OCV, POWER_SUPPLY_PROP_HI_POWER, Loading Loading @@ -2232,6 +2261,12 @@ static int qpnp_vm_bms_power_get_property(struct power_supply *psy, if (chip->dt.cfg_r_conn_mohm > 0) val->intval += chip->dt.cfg_r_conn_mohm; break; case POWER_SUPPLY_PROP_RESISTANCE_NOW: rc = get_batt_therm(chip, &value); if (rc < 0) value = BMS_DEFAULT_TEMP; val->intval = get_rbatt(chip, chip->calculated_soc, value); break; case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = get_prop_bms_current_now(chip); break; Loading Loading @@ -2442,6 +2477,10 @@ static void qpnp_vm_bms_ext_power_changed(struct power_supply *psy) battery_status_check(chip); battery_insertion_check(chip); mutex_lock(&chip->last_soc_mutex); battery_voltage_check(chip); mutex_unlock(&chip->last_soc_mutex); if (chip->reported_soc_in_use) reported_soc_check_status(chip); } Loading Loading @@ -2931,7 +2970,7 @@ static int calculate_initial_aging_comp(struct qpnp_bms_chip *chip) static int bms_load_hw_defaults(struct qpnp_bms_chip *chip) { u8 val, state, bms_en = 0; u8 val, bms_en = 0; u32 interval[2], count[2], fifo[2]; int rc; Loading Loading @@ -3029,14 +3068,10 @@ static int bms_load_hw_defaults(struct qpnp_bms_chip *chip) get_fifo_length(chip, S2_STATE, &fifo[1]); /* Force the BMS state to S2 at boot-up */ rc = get_fsm_state(chip, &state); if (rc) pr_err("Unable to get FSM state rc=%d\n", rc); if (rc || (state != S2_STATE)) { pr_debug("Forcing S2 state\n"); rc = force_fsm_state(chip, S2_STATE); if (rc) pr_err("Unable to set FSM state rc=%d\n", rc); if (rc) { pr_err("Unable to force S2 state rc=%d\n", rc); return rc; } rc = qpnp_read_wrapper(chip, &bms_en, chip->base + EN_CTL_REG, 1); Loading Loading @@ -3453,7 +3488,8 @@ static int set_battery_data(struct qpnp_bms_chip *chip) rc = of_batterydata_read_data(node, batt_data, battery_id); if (rc || !batt_data->pc_temp_ocv_lut || !batt_data->fcc_temp_lut || !batt_data->rbatt_sf_lut) { || !batt_data->rbatt_sf_lut || !batt_data->ibat_acc_lut) { pr_err("battery data load failed\n"); devm_kfree(chip->dev, batt_data->fcc_temp_lut); devm_kfree(chip->dev, batt_data->pc_temp_ocv_lut); Loading Loading
Documentation/devicetree/bindings/power/smb1360-charger-fg.txt +26 −5 Original line number Diff line number Diff line Loading @@ -117,11 +117,28 @@ Optional Properties: - qcom,config-hard-thresholds This property indicates if cold and hot thresholds need be modified. If it's not defined, default temp thresholds will be used. If this defined, it's required to specify the "qcom,hot-bat-decidegc" and "qcom,cold-bat-decidegc" values. - qcom,hot-bat-decidegc Hot battery temperature in decidegC. This property must be out of range [0, 55]. - qcom,cold-bat-decidegc Cold battery temperature in decidegC. This property must be out of range [0, 55]. "qcom,cold-bat-decidegc" values. If this defined, the serial properties (qcom,otp-hard-jeita-config, qcom,otp-hot-bat-decidegc, qcom,otp-cold-bat-decidegc) mustn't be defined. - qcom,hot-bat-decidegc Hot battery temperature in decidegC. This property should only be defined when "qcom,config-hard-thresholds" defined, and the value must be out of range [0, 55]. - qcom,cold-bat-decidegc Cold battery temperature in decidegC. This property should only be defined when "qcom,config-hard-thresholds" defined, and the value must be out of range [0, 55]. - qcom,otp-hard-jeita-config This property indicates if cold and hot thresholds need be modified. If it's not defined, default temp thresholds [0, 55] will be used. If this defined, it's required to specify the "qcom,otp-hot-bat-decidegc" and "qcom,otp-cold-bat-decidegc" values. If this is defined, the serial properties (qcom,config-hard-thresholds, qcom,hot-bat-decidegc, qcom,cold-bat-decidegc) mustn't be defined. - qcom,otp-hot-bat-decidegc Hot battery temperature in decidegC. This property could only be defined when qcom,config-otp-hard-jeita exist. - qcom,otp-cold-bat-decidegc Cold battery temperature in decidegC. This property could only be defined when qcom,config-otp-hard-jeita exist. - qcom,otp-hard-jeita-hysteresis This property contains two intergers to define the temperature hysteresis (in decidegC unit) for hard cold and hard hot. This property could only be defined when qcom,config-otp-hard-jeita exist. - qcom,soft-jeita-supported This property indicates whether soft jeita supported. - qcom,warm-bat-decidegc Warm battery temperature in decidegC. After hitting this threshold, "qcom,warm-bat-ma" defines maximum charging current and Loading Loading @@ -211,6 +228,10 @@ Example: qcom,soft-jeita-supported; qcom,warm-bat-decidegc = <450>; qcom,cool-bat-decidegc = <100>; qcom,otp-hard-jeita-config; qcom,otp-hot-bat-decidegc = <550>; qcom,otp-cold-bat-decidegc = <0>; qcom,otp-hard-jeita-hysteresis = <0, 50>; qcom,warm-bat-mv = <4100>; qcom,cool-bat-mv = <4100>; qcom,warm-bat-ma = <750>; Loading
drivers/power/qpnp-linear-charger.c +217 −67 Original line number Diff line number Diff line Loading @@ -40,8 +40,15 @@ /* USB CHARGER PATH peripheral register offsets */ #define USB_IN_VALID_MASK BIT(1) #define CHG_GONE_BIT BIT(2) #define USB_SUSP_REG 0x47 #define USB_SUSPEND_BIT BIT(0) #define USB_COMP_OVR1_REG 0xEA #define USBIN_LLIMIT_OK_MASK LBC_MASK(1, 0) #define USBIN_LLIMIT_OK_NO_OVERRIDE 0x00 #define USBIN_LLIMIT_OK_OVERRIDE_1 0x03 #define USB_OVP_TST5_REG 0xE7 #define CHG_GONE_OK_EN_BIT BIT(2) /* CHARGER peripheral register offset */ #define CHG_OPTION_REG 0x08 Loading Loading @@ -71,6 +78,8 @@ #define CHG_PERPH_RESET_CTRL3_REG 0xDA #define CHG_COMP_OVR1 0xEE #define CHG_VBAT_DET_OVR_MASK LBC_MASK(1, 0) #define CHG_TEST_LOOP_REG 0xE5 #define VIN_MIN_LOOP_DISABLE_BIT BIT(0) #define OVERRIDE_0 0x2 #define OVERRIDE_NONE 0x0 Loading Loading @@ -147,6 +156,7 @@ enum { CURRENT = BIT(2), SOC = BIT(3), PARALLEL = BIT(4), COLLAPSE = BIT(5), }; enum bpd_type { Loading Loading @@ -272,6 +282,7 @@ struct vddtrim_map vddtrim_map[] = { * @cfg_charger_detect_eoc: charger can detect end of charging * @cfg_disable_vbatdet_based_recharge: keep VBATDET comparator overriden to * low and VBATDET irq disabled. * @cfg_collapsible_chgr_support: support collapsible charger * @cfg_chgr_led_support: support charger led work. * @cfg_safe_current: battery safety current setting * @cfg_hot_batt_p: hot battery threshold setting Loading Loading @@ -324,7 +335,7 @@ struct qpnp_lbc_chip { bool fastchg_on; bool cfg_use_external_charger; bool cfg_chgr_led_support; bool cfg_bms_controlled_charging; bool non_collapsible_chgr_detected; unsigned int cfg_warm_bat_chg_ma; unsigned int cfg_cool_bat_chg_ma; unsigned int cfg_safe_voltage_mv; Loading @@ -333,6 +344,7 @@ struct qpnp_lbc_chip { unsigned int cfg_charger_detect_eoc; unsigned int cfg_disable_vbatdet_based_recharge; unsigned int cfg_batt_weak_voltage_uv; unsigned int cfg_collapsible_chgr_support; unsigned int cfg_warm_bat_mv; unsigned int cfg_cool_bat_mv; unsigned int cfg_hot_batt_p; Loading @@ -355,6 +367,7 @@ struct qpnp_lbc_chip { int usb_psy_ma; int delta_vddmax_uv; int init_trim_uv; struct delayed_work collapsible_detection_work; /* parallel-chg params */ int parallel_charging_enabled; Loading Loading @@ -598,9 +611,10 @@ static u8 qpnp_lbc_get_trim_val(struct qpnp_lbc_chip *chip) return vddtrim_map[i + 1].trim_val; } } i = 0; break; case 1: for (i = TRIM_CENTER; i <= 7; i++) { for (i = TRIM_CENTER; i < ARRAY_SIZE(vddtrim_map); i++) { if (vddtrim_map[i].trim_uv < chip->delta_vddmax_uv) { delta_uv = AVG(vddtrim_map[i].trim_uv, vddtrim_map[i - 1].trim_uv); Loading @@ -610,6 +624,7 @@ static u8 qpnp_lbc_get_trim_val(struct qpnp_lbc_chip *chip) return vddtrim_map[i].trim_val; } } i = ARRAY_SIZE(vddtrim_map) - 1; break; } Loading @@ -634,6 +649,24 @@ static int qpnp_lbc_is_usb_chg_plugged_in(struct qpnp_lbc_chip *chip) return (usbin_valid_rt_sts & USB_IN_VALID_MASK) ? 1 : 0; } static int qpnp_lbc_is_chg_gone(struct qpnp_lbc_chip *chip) { u8 rt_sts; int rc; rc = qpnp_lbc_read(chip, chip->usb_chgpth_base + INT_RT_STS_REG, &rt_sts, 1); if (rc) { pr_err("spmi read failed: addr=0x%04x, rc=%d\n", chip->usb_chgpth_base + INT_RT_STS_REG, rc); return rc; } pr_debug("rt_sts 0x%x\n", rt_sts); return (rt_sts & CHG_GONE_BIT) ? 1 : 0; } static int qpnp_lbc_charger_enable(struct qpnp_lbc_chip *chip, int reason, int enable) { Loading Loading @@ -700,10 +733,9 @@ static int qpnp_lbc_bat_if_configure_btc(struct qpnp_lbc_chip *chip) mask |= BTC_COLD_MASK; } if (!chip->cfg_btc_disabled) { mask |= BTC_COMP_EN_MASK; if (!chip->cfg_btc_disabled) btc_cfg |= BTC_COMP_EN_MASK; } pr_debug("BTC configuration mask=%x\n", btc_cfg); Loading @@ -716,6 +748,75 @@ static int qpnp_lbc_bat_if_configure_btc(struct qpnp_lbc_chip *chip) return rc; } static int qpnp_chg_collapsible_chgr_config(struct qpnp_lbc_chip *chip, bool enable) { u8 reg_val; int rc; pr_debug("Configure for %scollapsible charger\n", enable ? "" : "non-"); /* * The flow to enable/disable the collapsible charger configuration: * Enable: Override USBIN_LLIMIT_OK --> * Disable VIN_MIN comparator --> * Enable CHG_GONE comparator * Disable: Enable VIN_MIN comparator --> * Enable USBIN_LLIMIT_OK --> * Disable CHG_GONE comparator */ if (enable) { /* Override USBIN_LLIMIT_OK */ reg_val = USBIN_LLIMIT_OK_OVERRIDE_1; rc = __qpnp_lbc_secure_masked_write(chip->spmi, chip->usb_chgpth_base, USB_COMP_OVR1_REG, USBIN_LLIMIT_OK_MASK, reg_val); if (rc) { pr_err("Failed to override USB_LLIMIT_OK rc = %d\n", rc); return rc; } } /* Configure VIN_MIN comparator */ rc = __qpnp_lbc_secure_masked_write(chip->spmi, chip->chgr_base, CHG_TEST_LOOP_REG, VIN_MIN_LOOP_DISABLE_BIT, enable ? VIN_MIN_LOOP_DISABLE_BIT : 0); if (rc) { pr_err("Failed to %s VIN_MIN comparator rc = %d\n", enable ? "disable" : "enable", rc); return rc; } if (!enable) { /* Enable USBIN_LLIMIT_OK */ reg_val = USBIN_LLIMIT_OK_NO_OVERRIDE; rc = __qpnp_lbc_secure_masked_write(chip->spmi, chip->usb_chgpth_base, USB_COMP_OVR1_REG, USBIN_LLIMIT_OK_MASK, reg_val); if (rc) { pr_err("Failed to override USB_LLIMIT_OK rc = %d\n", rc); return rc; } } /* Configure CHG_GONE comparator */ reg_val = enable ? CHG_GONE_OK_EN_BIT : 0; rc = __qpnp_lbc_secure_masked_write(chip->spmi, chip->usb_chgpth_base, USB_OVP_TST5_REG, CHG_GONE_OK_EN_BIT, reg_val); if (rc) { pr_err("Failed to write CHG_GONE comparator rc = %d\n", rc); return rc; } return 0; } #define QPNP_LBC_VBATWEAK_MIN_UV 3000000 #define QPNP_LBC_VBATWEAK_MAX_UV 3581250 #define QPNP_LBC_VBATWEAK_STEP_UV 18750 Loading Loading @@ -1218,7 +1319,7 @@ static int get_prop_current_now(struct qpnp_lbc_chip *chip) static int get_prop_capacity(struct qpnp_lbc_chip *chip) { union power_supply_propval ret = {0,}; int soc, battery_status, charger_in; int soc; if (chip->fake_battery_soc >= 0) return chip->fake_battery_soc; Loading @@ -1229,33 +1330,6 @@ static int get_prop_capacity(struct qpnp_lbc_chip *chip) if (chip->bms_psy) { chip->bms_psy->get_property(chip->bms_psy, POWER_SUPPLY_PROP_CAPACITY, &ret); mutex_lock(&chip->chg_enable_lock); if (chip->chg_done) chip->bms_psy->get_property(chip->bms_psy, POWER_SUPPLY_PROP_CAPACITY, &ret); battery_status = get_prop_batt_status(chip); charger_in = qpnp_lbc_is_usb_chg_plugged_in(chip); /* reset chg_done flag if capacity not 100% */ if (ret.intval < 100 && chip->chg_done) { chip->chg_done = false; power_supply_changed(&chip->batt_psy); } if (battery_status != POWER_SUPPLY_STATUS_CHARGING && charger_in && !chip->cfg_charging_disabled && chip->cfg_soc_resume_limit && ret.intval <= chip->cfg_soc_resume_limit && !chip->cfg_bms_controlled_charging) { pr_debug("resuming charging at %d%% soc\n", ret.intval); if (!chip->cfg_disable_vbatdet_based_recharge) qpnp_lbc_vbatdet_override(chip, OVERRIDE_0); qpnp_lbc_charger_enable(chip, SOC, 1); } mutex_unlock(&chip->chg_enable_lock); soc = ret.intval; if (soc == 0) { if (!qpnp_lbc_is_usb_chg_plugged_in(chip)) Loading Loading @@ -1523,10 +1597,10 @@ static int qpnp_batt_power_set_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_STATUS: mutex_lock(&chip->chg_enable_lock); if (val->intval == POWER_SUPPLY_STATUS_FULL && !chip->cfg_float_charge) { switch (val->intval) { case POWER_SUPPLY_STATUS_FULL: if (chip->cfg_float_charge) break; /* Disable charging */ rc = qpnp_lbc_charger_enable(chip, SOC, 0); if (rc) Loading @@ -1551,17 +1625,13 @@ static int qpnp_batt_power_set_property(struct power_supply *psy, qpnp_lbc_enable_irq(chip, &chip->irqs[CHG_VBAT_DET_LO]); } } if (chip->cfg_bms_controlled_charging) { switch (val->intval) { break; case POWER_SUPPLY_STATUS_CHARGING: chip->chg_done = false; pr_debug("resuming charging by bms\n"); if (!chip->cfg_disable_vbatdet_based_recharge) qpnp_lbc_vbatdet_override(chip, OVERRIDE_0); qpnp_lbc_vbatdet_override(chip, OVERRIDE_0); qpnp_lbc_charger_enable(chip, SOC, 1); break; case POWER_SUPPLY_STATUS_DISCHARGING: Loading @@ -1572,8 +1642,6 @@ static int qpnp_batt_power_set_property(struct power_supply *psy, default: break; } } mutex_unlock(&chip->chg_enable_lock); break; case POWER_SUPPLY_PROP_COOL_TEMP: Loading Loading @@ -2105,7 +2173,6 @@ static int show_lbc_config(struct seq_file *m, void *data) "cfg_use_fake_battery\t=\t%d\n" "cfg_use_external_charger\t=\t%d\n" "cfg_chgr_led_support\t=\t%d\n" "cfg_bms_controlled_charging\t=\t%d\n" "cfg_warm_bat_chg_ma\t=\t%d\n" "cfg_cool_bat_chg_ma\t=\t%d\n" "cfg_safe_voltage_mv\t=\t%d\n" Loading @@ -2113,6 +2180,7 @@ static int show_lbc_config(struct seq_file *m, void *data) "cfg_min_voltage_mv\t=\t%d\n" "cfg_charger_detect_eoc\t=\t%d\n" "cfg_disable_vbatdet_based_recharge\t=\t%d\n" "cfg_collapsible_chgr_support\t=\t%d\n" "cfg_batt_weak_voltage_uv\t=\t%d\n" "cfg_warm_bat_mv\t=\t%d\n" "cfg_cool_bat_mv\t=\t%d\n" Loading @@ -2131,7 +2199,6 @@ static int show_lbc_config(struct seq_file *m, void *data) chip->cfg_use_fake_battery, chip->cfg_use_external_charger, chip->cfg_chgr_led_support, chip->cfg_bms_controlled_charging, chip->cfg_warm_bat_chg_ma, chip->cfg_cool_bat_chg_ma, chip->cfg_safe_voltage_mv, Loading @@ -2139,6 +2206,7 @@ static int show_lbc_config(struct seq_file *m, void *data) chip->cfg_min_voltage_mv, chip->cfg_charger_detect_eoc, chip->cfg_disable_vbatdet_based_recharge, chip->cfg_collapsible_chgr_support, chip->cfg_batt_weak_voltage_uv, chip->cfg_warm_bat_mv, chip->cfg_cool_bat_mv, Loading Loading @@ -2284,9 +2352,10 @@ static int qpnp_charger_read_dt_props(struct qpnp_lbc_chip *chip) of_property_read_bool(chip->spmi->dev.of_node, "qcom,chgr-led-support"); chip->cfg_bms_controlled_charging = /* Get the collapsible charger support property */ chip->cfg_collapsible_chgr_support = of_property_read_bool(chip->spmi->dev.of_node, "qcom,bms-controlled-charging"); "qcom,collapsible-chgr-support"); /* Disable charging when faking battery values */ if (chip->cfg_use_fake_battery) Loading Loading @@ -2343,17 +2412,50 @@ static int qpnp_charger_read_dt_props(struct qpnp_lbc_chip *chip) chip->cfg_charging_disabled, chip->cfg_use_fake_battery, chip->cfg_float_charge); pr_debug("charger-detect-eoc=%d, disable-vbatdet-based-recharge=%d, chgr-led-support=%d, bms-controlled-charging=%d\n", pr_debug("charger-detect-eoc=%d, disable-vbatdet-based-recharge=%d, chgr-led-support=%d\n", chip->cfg_charger_detect_eoc, chip->cfg_disable_vbatdet_based_recharge, chip->cfg_chgr_led_support, chip->cfg_bms_controlled_charging); pr_debug("use-external-charger=%d, thermal_levels=%d\n", chip->cfg_chgr_led_support); pr_debug("collapsible-chg-support=%d, use-external-charger=%d, thermal_levels=%d\n", chip->cfg_collapsible_chgr_support, chip->cfg_use_external_charger, chip->cfg_thermal_levels); return rc; } #define CHG_REMOVAL_DETECT_DLY_MS 300 static irqreturn_t qpnp_lbc_chg_gone_irq_handler(int irq, void *_chip) { struct qpnp_lbc_chip *chip = _chip; int chg_gone; if (chip->cfg_collapsible_chgr_support) { chg_gone = qpnp_lbc_is_chg_gone(chip); pr_debug("chg-gone triggered, rt_sts: %d\n", chg_gone); if (chg_gone) { /* * Disable charger to prevent fastchg irq storming * if a non-collapsible charger is being used. */ pr_debug("disable charging for non-collapsbile charger\n"); qpnp_lbc_charger_enable(chip, COLLAPSE, 0); qpnp_lbc_disable_irq(chip, &chip->irqs[USBIN_VALID]); qpnp_lbc_disable_irq(chip, &chip->irqs[USB_CHG_GONE]); qpnp_chg_collapsible_chgr_config(chip, 0); /* * Check after a delay if the charger is still * inserted. It decides if a non-collapsible * charger is being used, or charger has been * removed. */ schedule_delayed_work(&chip->collapsible_detection_work, msecs_to_jiffies(CHG_REMOVAL_DETECT_DLY_MS)); } } return IRQ_HANDLED; } static irqreturn_t qpnp_lbc_usbin_valid_irq_handler(int irq, void *_chip) { struct qpnp_lbc_chip *chip = _chip; Loading @@ -2372,6 +2474,11 @@ static irqreturn_t qpnp_lbc_usbin_valid_irq_handler(int irq, void *_chip) qpnp_lbc_set_appropriate_current(chip); spin_unlock_irqrestore(&chip->ibat_change_lock, flags); if (chip->cfg_collapsible_chgr_support) chip->non_collapsible_chgr_detected = false; if (chip->supported_feature_flag & VDD_TRIM_SUPPORTED) alarm_try_to_cancel(&chip->vddtrim_alarm); } else { /* * Override VBAT_DET comparator to start charging Loading @@ -2380,6 +2487,16 @@ static irqreturn_t qpnp_lbc_usbin_valid_irq_handler(int irq, void *_chip) if (!chip->cfg_disable_vbatdet_based_recharge) qpnp_lbc_vbatdet_override(chip, OVERRIDE_0); /* * If collapsible charger supported, enable chgr_gone * irq, and configure for collapsible charger. */ if (chip->cfg_collapsible_chgr_support && !chip->non_collapsible_chgr_detected) { qpnp_lbc_enable_irq(chip, &chip->irqs[USB_CHG_GONE]); qpnp_chg_collapsible_chgr_config(chip, 1); } /* * Enable SOC based charging to make sure * charging gets enabled on USB insertion Loading Loading @@ -2685,6 +2802,9 @@ static int qpnp_lbc_request_irqs(struct qpnp_lbc_chip *chip) SPMI_REQUEST_IRQ(chip, USBIN_VALID, rc, usbin_valid, 0, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 1); SPMI_REQUEST_IRQ(chip, USB_CHG_GONE, rc, chg_gone, 0, IRQF_TRIGGER_RISING, 1); SPMI_REQUEST_IRQ(chip, USB_OVER_TEMP, rc, usb_overtemp, 0, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 0); Loading Loading @@ -2720,6 +2840,8 @@ static int qpnp_lbc_get_irqs(struct qpnp_lbc_chip *chip, u8 subtype, USBIN_VALID, usbin-valid); SPMI_GET_IRQ_RESOURCE(chip, rc, spmi_resource, USB_OVER_TEMP, usb-over-temp); SPMI_GET_IRQ_RESOURCE(chip, rc, spmi_resource, USB_CHG_GONE, chg-gone); break; }; Loading @@ -2735,9 +2857,34 @@ static void determine_initial_status(struct qpnp_lbc_chip *chip) * Set USB psy online to avoid userspace from shutting down if battery * capacity is at zero and no chargers online. */ if (chip->usb_present) if (chip->usb_present) { if (chip->cfg_collapsible_chgr_support && !chip->non_collapsible_chgr_detected) { qpnp_lbc_enable_irq(chip, &chip->irqs[USB_CHG_GONE]); qpnp_chg_collapsible_chgr_config(chip, 1); } power_supply_set_online(chip->usb_psy, 1); } } static void qpnp_lbc_collapsible_detection_work(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); struct qpnp_lbc_chip *chip = container_of(dwork, struct qpnp_lbc_chip, collapsible_detection_work); if (qpnp_lbc_is_usb_chg_plugged_in(chip)) { chip->non_collapsible_chgr_detected = true; pr_debug("Non-collapsible charger detected\n"); } else { chip->non_collapsible_chgr_detected = false; pr_debug("Charger removal detected\n"); } qpnp_lbc_charger_enable(chip, COLLAPSE, 1); qpnp_lbc_enable_irq(chip, &chip->irqs[USBIN_VALID]); } #define IBAT_TRIM -300 static void qpnp_lbc_vddtrim_work_fn(struct work_struct *work) Loading Loading @@ -3041,6 +3188,8 @@ static int qpnp_lbc_main_probe(struct spmi_device *spmi) spin_lock_init(&chip->irq_lock); INIT_WORK(&chip->vddtrim_work, qpnp_lbc_vddtrim_work_fn); alarm_init(&chip->vddtrim_alarm, ALARM_REALTIME, vddtrim_callback); INIT_DELAYED_WORK(&chip->collapsible_detection_work, qpnp_lbc_collapsible_detection_work); /* Get all device-tree properties */ rc = qpnp_charger_read_dt_props(chip); Loading Loading @@ -3226,6 +3375,7 @@ static int qpnp_lbc_remove(struct spmi_device *spmi) alarm_cancel(&chip->vddtrim_alarm); cancel_work_sync(&chip->vddtrim_work); } cancel_delayed_work_sync(&chip->collapsible_detection_work); debugfs_remove_recursive(chip->debug_root); if (chip->bat_if_base) power_supply_unregister(&chip->batt_psy); Loading
drivers/power/qpnp-vm-bms.c +59 −23 Original line number Diff line number Diff line Loading @@ -35,7 +35,7 @@ #include <linux/qpnp/qpnp-adc.h> #include <linux/of_batterydata.h> #include <linux/batterydata-interface.h> #include <linux/qpnp-revid.h> #include <linux/qpnp/qpnp-revid.h> #include <uapi/linux/vm_bms.h> #define _BMS_MASK(BITS, POS) \ Loading Loading @@ -244,6 +244,7 @@ struct qpnp_bms_chip { u16 charge_cycles; unsigned int start_soc; unsigned int end_soc; unsigned int chg_start_soc; struct bms_battery_data *batt_data; struct bms_dt_cfg dt; Loading Loading @@ -962,7 +963,7 @@ static int lookup_soc_ocv(struct qpnp_bms_chip *chip, int ocv_uv, int batt_temp) if (chip->batt_data->ibat_acc_lut) { /* Apply ACC logic only if we discharging */ if (!is_battery_charging(chip) && chip->current_now > 0) { if (chip->current_now > 0) { /* * IBAT averaging is disabled at low temp. Loading Loading @@ -1607,7 +1608,9 @@ static int report_vm_bms_soc(struct qpnp_bms_chip *chip) * avoid overflows when charging continues for extended periods */ if (charging && chip->last_soc != -EINVAL) { if (chip->charge_start_tm_sec == 0) { if (chip->charge_start_tm_sec == 0 || (chip->catch_up_time_sec == 0 && (abs(soc - chip->last_soc) >= MIN_SOC_UUC))) { /* * calculating soc for the first time * after start of chg. Initialize catchup time Loading @@ -1619,17 +1622,25 @@ static int report_vm_bms_soc(struct qpnp_bms_chip *chip) else chip->catch_up_time_sec = SOC_CATCHUP_SEC_MAX; chip->chg_start_soc = chip->last_soc; if (chip->catch_up_time_sec < 0) chip->catch_up_time_sec = 0; chip->charge_start_tm_sec = last_change_sec; pr_debug("chg_start_soc=%d charge_start_tm_sec=%d catch_up_time_sec=%d\n", chip->chg_start_soc, chip->charge_start_tm_sec, chip->catch_up_time_sec); } charge_time_sec = min(SOC_CATCHUP_SEC_MAX, (int)last_change_sec - chip->charge_start_tm_sec); /* end catchup if calculated soc and last soc are same */ if (chip->last_soc == soc) if (chip->last_soc == soc) { chip->catch_up_time_sec = 0; chip->chg_start_soc = chip->last_soc; } } if (chip->last_soc != -EINVAL) { Loading @@ -1647,7 +1658,7 @@ static int report_vm_bms_soc(struct qpnp_bms_chip *chip) else if (chip->last_soc < soc && soc != 100) soc = scale_soc_while_chg(chip, charge_time_sec, chip->catch_up_time_sec, soc, chip->last_soc); soc, chip->chg_start_soc); /* * if the battery is close to cutoff or if the batt_temp Loading Loading @@ -1963,6 +1974,11 @@ static void calculate_reported_soc(struct qpnp_bms_chip *chip) { union power_supply_propval ret = {0,}; if (chip->last_soc < 0) { pr_debug("last_soc is not ready, return\n"); return; } if (chip->reported_soc > chip->last_soc) { /*send DISCHARGING status if the reported_soc drops from 100 */ if (chip->reported_soc == 100) { Loading Loading @@ -2022,13 +2038,26 @@ static int clamp_soc_based_on_voltage(struct qpnp_bms_chip *chip, int soc) } } static void battery_voltage_check(struct qpnp_bms_chip *chip) { int rc, vbat_uv = 0; rc = get_battery_voltage(chip, &vbat_uv); if (rc < 0) { pr_err("Failed to read battery-voltage rc=%d\n", rc); } else { very_low_voltage_check(chip, vbat_uv); cv_voltage_check(chip, vbat_uv); } } #define UI_SOC_CATCHUP_TIME (60) static void monitor_soc_work(struct work_struct *work) { struct qpnp_bms_chip *chip = container_of(work, struct qpnp_bms_chip, monitor_soc_work.work); int rc, vbat_uv = 0, new_soc = 0, batt_temp; int rc, new_soc = 0, batt_temp; bms_stay_awake(&chip->vbms_soc_wake_source); Loading @@ -2044,13 +2073,7 @@ static void monitor_soc_work(struct work_struct *work) chip->last_soc = -EINVAL; new_soc = 100; } else { rc = get_battery_voltage(chip, &vbat_uv); if (rc < 0) { pr_err("Failed to read battery-voltage rc=%d\n", rc); } else { very_low_voltage_check(chip, vbat_uv); cv_voltage_check(chip, vbat_uv); } battery_voltage_check(chip); if (chip->dt.cfg_use_voltage_soc) { calculate_soc_from_voltage(chip); Loading @@ -2075,6 +2098,11 @@ static void monitor_soc_work(struct work_struct *work) pr_debug("SOC changed! new_soc=%d prev_soc=%d\n", new_soc, chip->calculated_soc); chip->calculated_soc = new_soc; /* * To recalculate the catch-up time, clear it * when SOC changes. */ chip->catch_up_time_sec = 0; if (chip->calculated_soc == 100) /* update last_soc immediately */ Loading Loading @@ -2180,6 +2208,7 @@ static enum power_supply_property bms_power_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_RESISTANCE, POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE, POWER_SUPPLY_PROP_RESISTANCE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_VOLTAGE_OCV, POWER_SUPPLY_PROP_HI_POWER, Loading Loading @@ -2232,6 +2261,12 @@ static int qpnp_vm_bms_power_get_property(struct power_supply *psy, if (chip->dt.cfg_r_conn_mohm > 0) val->intval += chip->dt.cfg_r_conn_mohm; break; case POWER_SUPPLY_PROP_RESISTANCE_NOW: rc = get_batt_therm(chip, &value); if (rc < 0) value = BMS_DEFAULT_TEMP; val->intval = get_rbatt(chip, chip->calculated_soc, value); break; case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = get_prop_bms_current_now(chip); break; Loading Loading @@ -2442,6 +2477,10 @@ static void qpnp_vm_bms_ext_power_changed(struct power_supply *psy) battery_status_check(chip); battery_insertion_check(chip); mutex_lock(&chip->last_soc_mutex); battery_voltage_check(chip); mutex_unlock(&chip->last_soc_mutex); if (chip->reported_soc_in_use) reported_soc_check_status(chip); } Loading Loading @@ -2931,7 +2970,7 @@ static int calculate_initial_aging_comp(struct qpnp_bms_chip *chip) static int bms_load_hw_defaults(struct qpnp_bms_chip *chip) { u8 val, state, bms_en = 0; u8 val, bms_en = 0; u32 interval[2], count[2], fifo[2]; int rc; Loading Loading @@ -3029,14 +3068,10 @@ static int bms_load_hw_defaults(struct qpnp_bms_chip *chip) get_fifo_length(chip, S2_STATE, &fifo[1]); /* Force the BMS state to S2 at boot-up */ rc = get_fsm_state(chip, &state); if (rc) pr_err("Unable to get FSM state rc=%d\n", rc); if (rc || (state != S2_STATE)) { pr_debug("Forcing S2 state\n"); rc = force_fsm_state(chip, S2_STATE); if (rc) pr_err("Unable to set FSM state rc=%d\n", rc); if (rc) { pr_err("Unable to force S2 state rc=%d\n", rc); return rc; } rc = qpnp_read_wrapper(chip, &bms_en, chip->base + EN_CTL_REG, 1); Loading Loading @@ -3453,7 +3488,8 @@ static int set_battery_data(struct qpnp_bms_chip *chip) rc = of_batterydata_read_data(node, batt_data, battery_id); if (rc || !batt_data->pc_temp_ocv_lut || !batt_data->fcc_temp_lut || !batt_data->rbatt_sf_lut) { || !batt_data->rbatt_sf_lut || !batt_data->ibat_acc_lut) { pr_err("battery data load failed\n"); devm_kfree(chip->dev, batt_data->fcc_temp_lut); devm_kfree(chip->dev, batt_data->pc_temp_ocv_lut); Loading