Loading Documentation/devicetree/bindings/power/stc311x-fg.txt +23 −0 Original line number Diff line number Diff line Loading @@ -103,6 +103,25 @@ a 0-100 percentage representing the amount of charge left in the battery. Value type: Boolean Definition: This is to enable soc correction algorithm. -st,empty-soc-uv Usage: Optional Value type: <u32> in uV Definition: This is the battery voltage threshold to reporting 0% SOC. If this property is present then 'qcom,fg-adc_tm' and 'qcom,fg-vadc' are required properties. -qcom,fg-adc_tm Usage: Optional Value type: <phandle> Definition: ADC_TM device's phandle to receive notifications when battery voltage crosses configured thresholds. - qcom,fg-vadc Usage: Optional Value type: <phandle> Definition: VADC device's phandle to read battery voltage using ADC channel. Example: st-fg@70 { compatible = "st,stc3117"; Loading @@ -127,5 +146,9 @@ Example: 70 80 90 100>; st,vmtemp-tbl = /bits/ 16 <85 90 100 160 320 440 840> st,temp-tbl = /bits/ 16 <60 40 25 10 0 (-10) (-20)>; st,empty-soc-uv = <3300000>; qcom,fg-vadc = <&pm8916_vadc>; qcom,fg-adc_tm = <&pm8916_adc_tm>; }; arch/arm/boot/dts/qcom/msm8909w-pm8916-mtp-ext-chg-fg.dtsi +6 −2 Original line number Diff line number Diff line Loading @@ -149,14 +149,14 @@ st-fg@70 { compatible = "st,stc3117"; reg = <0x70>; st,rbatt-mohm = <427>; st,rbatt-mohm = <477>; st,nom-capacity-mah = <450>; st,rsense-mohm = <25>; st,float-voltage-mv = <4200>; st,alarm-soc = <10>; st,alarm-voltage-mv = <3400>; st,term-current-ma = <50>; st,relax-current-ma = <25>; st,relax-current-ma = <100>; st,adaptive-capacity-tbl = /bits/ 16 <0x00 0x00 0x00 0x05 0x0d 0x2a 0x47>; st,ocv-tbl = Loading @@ -170,5 +170,9 @@ /bits/ 16 <0x55 0x5a 0x64 0xa0 0x140 0x1b8 0x348>; st,temp-tbl = /bits/ 16 <60 40 25 10 0 (-10) (-20)>; st,empty-soc-uv = <3300000>; qcom,fg-vadc = <&pm8916_vadc>; qcom,fg-adc_tm = <&pm8916_adc_tm>; }; }; drivers/power/stc3117_battery.c +189 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ #include <linux/slab.h> #include <linux/power_supply.h> #include <linux/qpnp/qpnp-adc.h> #define MODE_REG 0x0 #define VMODE_BIT BIT(0) #define FORCE_CD_BIT BIT(2) Loading Loading @@ -119,7 +121,7 @@ do { \ struct fg_data { int soc; int hr_soc; u16 voltage_mv; s16 voltage_mv; s16 current_ma; s16 avg_current_ma; s16 temperature; Loading Loading @@ -172,6 +174,7 @@ struct stc311x_chip { int cfg_rbatt_mohm; int cfg_term_current_ma; int cfg_float_voltage_mv; int cfg_empty_soc_uv; bool cfg_force_vmode; bool cfg_enable_soc_correction; Loading @@ -192,10 +195,17 @@ struct stc311x_chip { struct i2c_client *client; struct device *dev; struct delayed_work soc_calc_work; struct delayed_work cutoff_check_work; struct fg_wakeup_source soc_calc_wake_source; struct fg_wakeup_source cutoff_wake_source; struct dentry *debug_root; struct mutex read_write_lock; struct power_supply bms_psy; /* ADC notification */ struct qpnp_adc_tm_chip *adc_tm_dev; struct qpnp_vadc_chip *vadc_dev; struct qpnp_adc_tm_btm_param vbat_cutoff_params; }; static char *fg_supplicants[] = { Loading Loading @@ -658,11 +668,145 @@ static int stc311x_start(struct stc311x_chip *chip) return rc; } static int stc311x_force_soc(struct stc311x_chip *chip, int soc, int vbat_uv) { int rc; rc = stc311x_write_param(chip, SOC_REG, soc); if (rc) pr_err("Failed to set SOC to %d rc=%d\n", soc, rc); else pr_warn("forcing SOC = %d vbat = %duV\n", soc, vbat_uv); return rc; } static int get_battery_voltage_adc(struct stc311x_chip *chip, int *result_uv) { int rc; struct qpnp_vadc_result adc_result; if (!chip->vadc_dev) return -EINVAL; rc = qpnp_vadc_read(chip->vadc_dev, VBAT_SNS, &adc_result); if (rc) { pr_err("error reading adc channel = %d, rc = %d\n", VBAT_SNS, rc); return rc; } pr_debug("mvolts phy=%lld meas=0x%llx\n", adc_result.physical, adc_result.measurement); *result_uv = (int)adc_result.physical; return 0; } #define VBATT_ERROR_MARGIN_UV 20000 #define CUTOFF_MARGIN_UV 100000 #define CUTOFF_DELAY 30000 static void btm_notify_vbat(enum qpnp_tm_state state, void *ctx) { struct stc311x_chip *chip = ctx; int vbat_uv; int rc; rc = get_battery_voltage_adc(chip, &vbat_uv); if (rc) { pr_err("failed to read battery voltage rc=%d\n", rc); goto out; } pr_debug("vbat is at %duV\n", vbat_uv); if (state == ADC_TM_LOW_STATE) { pr_debug("low voltage btm notification triggered\n"); if (vbat_uv < (chip->vbat_cutoff_params.low_thr + VBATT_ERROR_MARGIN_UV)) { fg_stay_awake(&chip->cutoff_wake_source); schedule_delayed_work(&chip->cutoff_check_work, msecs_to_jiffies(CUTOFF_DELAY)); chip->vbat_cutoff_params.state_request = ADC_TM_HIGH_THR_ENABLE; } else { pr_debug("faulty btm trigger, discarding\n"); } } else if (state == ADC_TM_HIGH_STATE) { pr_debug("high voltage btm notification triggered\n"); if (vbat_uv >= chip->vbat_cutoff_params.high_thr) { chip->vbat_cutoff_params.state_request = ADC_TM_LOW_THR_ENABLE; cancel_delayed_work_sync(&chip->cutoff_check_work); fg_relax(&chip->cutoff_wake_source); } else { pr_debug("faulty btm trigger, discarding\n"); } } else { pr_debug("unknown voltage notification state: %d\n", state); } out: qpnp_adc_tm_channel_measure(chip->adc_tm_dev, &chip->vbat_cutoff_params); } static int stc311x_vbat_cutoff_monitor(struct stc311x_chip *chip) { int rc; chip->vbat_cutoff_params.low_thr = chip->cfg_empty_soc_uv + CUTOFF_MARGIN_UV; chip->vbat_cutoff_params.high_thr = chip->cfg_empty_soc_uv + CUTOFF_MARGIN_UV + VBATT_ERROR_MARGIN_UV; chip->vbat_cutoff_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE; chip->vbat_cutoff_params.channel = VBAT_SNS; chip->vbat_cutoff_params.btm_ctx = chip; chip->vbat_cutoff_params.timer_interval = ADC_MEAS1_INTERVAL_16S; chip->vbat_cutoff_params.threshold_notification = &btm_notify_vbat; rc = qpnp_adc_tm_channel_measure(chip->adc_tm_dev, &chip->vbat_cutoff_params); if (rc) { pr_err("adc-tm setup failed: %d\n", rc); return rc; } pr_debug("set low thr to %d and high to %d\n", chip->vbat_cutoff_params.low_thr, chip->vbat_cutoff_params.high_thr); return 0; } int stc311x_parse_dt(struct stc311x_chip *chip) { int rc = 0; int prop_len; chip->cfg_empty_soc_uv = -EINVAL; OF_PROP_READ(chip, chip->cfg_empty_soc_uv, "empty-soc-uv", rc, 1); if (chip->cfg_empty_soc_uv > 0) { chip->vadc_dev = qpnp_get_vadc(chip->dev, "fg"); if (IS_ERR(chip->vadc_dev)) { rc = PTR_ERR(chip->vadc_dev); if (rc != -EPROBE_DEFER) pr_err("vadc not found defer probe\n"); return rc; } chip->adc_tm_dev = qpnp_get_adc_tm(chip->dev, "fg"); if (IS_ERR(chip->adc_tm_dev)) { rc = PTR_ERR(chip->adc_tm_dev); if (rc != -EPROBE_DEFER) pr_err("adc-tm property missing, rc=%d\n", rc); return rc; } rc = stc311x_vbat_cutoff_monitor(chip); if (rc) pr_err("failed to configure vbat cutoff monitor rc=%d\n", rc); } OF_PROP_READ(chip, chip->cfg_rbatt_mohm, "rbatt-mohm", rc, 0); OF_PROP_READ(chip, chip->cfg_cnom_mah, Loading Loading @@ -1283,6 +1427,45 @@ static void soc_calc_work_fn(struct work_struct *work) schedule_delayed_work(&chip->soc_calc_work, msecs_to_jiffies(delay)); } static void cutoff_check_work_fn(struct work_struct *work) { int rc; int vbat_uv; struct stc311x_chip *chip = container_of(work, struct stc311x_chip, cutoff_check_work.work); rc = get_battery_voltage_adc(chip, &vbat_uv); if (rc) { pr_err("failed to read VBAT rc=%d\n", rc); goto out; } pr_debug("vbat is at %duV\n", vbat_uv); /* * release wakeup source and return if battery voltage crosses high * threhold limit. */ if (vbat_uv >= (chip->vbat_cutoff_params.high_thr + VBATT_ERROR_MARGIN_UV)) { fg_relax(&chip->cutoff_wake_source); return; } if (vbat_uv <= (chip->cfg_empty_soc_uv)) { rc = stc311x_force_soc(chip, 0, vbat_uv); if (rc) goto out; stc311x_process_fg_task(chip); return; } out: schedule_delayed_work(&chip->cutoff_check_work, msecs_to_jiffies(CUTOFF_DELAY)); } static enum power_supply_property stc311x_bms_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, Loading Loading @@ -1546,6 +1729,7 @@ static int stc311x_probe(struct i2c_client *client, chip->client = client; mutex_init(&chip->read_write_lock); INIT_DELAYED_WORK(&chip->soc_calc_work, soc_calc_work_fn); INIT_DELAYED_WORK(&chip->cutoff_check_work, cutoff_check_work_fn); /* read chip id */ rc = stc311x_read_raw(chip, ID_REG, ®, 1); Loading @@ -1568,6 +1752,9 @@ static int stc311x_probe(struct i2c_client *client, chip->soc_calc_wake_source.disabled = 1; wakeup_source_init(&chip->soc_calc_wake_source.source, "fg_soc_calc_wake"); chip->cutoff_wake_source.disabled = 1; wakeup_source_init(&chip->cutoff_wake_source.source, "fg_cutoff_wake"); rc = stc311x_start(chip); if (rc) { pr_err("failed to start rc=%d\n", rc); Loading Loading @@ -1597,6 +1784,7 @@ static int stc311x_probe(struct i2c_client *client, fail: wakeup_source_trash(&chip->soc_calc_wake_source.source); wakeup_source_trash(&chip->cutoff_wake_source.source); return rc; } Loading Loading
Documentation/devicetree/bindings/power/stc311x-fg.txt +23 −0 Original line number Diff line number Diff line Loading @@ -103,6 +103,25 @@ a 0-100 percentage representing the amount of charge left in the battery. Value type: Boolean Definition: This is to enable soc correction algorithm. -st,empty-soc-uv Usage: Optional Value type: <u32> in uV Definition: This is the battery voltage threshold to reporting 0% SOC. If this property is present then 'qcom,fg-adc_tm' and 'qcom,fg-vadc' are required properties. -qcom,fg-adc_tm Usage: Optional Value type: <phandle> Definition: ADC_TM device's phandle to receive notifications when battery voltage crosses configured thresholds. - qcom,fg-vadc Usage: Optional Value type: <phandle> Definition: VADC device's phandle to read battery voltage using ADC channel. Example: st-fg@70 { compatible = "st,stc3117"; Loading @@ -127,5 +146,9 @@ Example: 70 80 90 100>; st,vmtemp-tbl = /bits/ 16 <85 90 100 160 320 440 840> st,temp-tbl = /bits/ 16 <60 40 25 10 0 (-10) (-20)>; st,empty-soc-uv = <3300000>; qcom,fg-vadc = <&pm8916_vadc>; qcom,fg-adc_tm = <&pm8916_adc_tm>; };
arch/arm/boot/dts/qcom/msm8909w-pm8916-mtp-ext-chg-fg.dtsi +6 −2 Original line number Diff line number Diff line Loading @@ -149,14 +149,14 @@ st-fg@70 { compatible = "st,stc3117"; reg = <0x70>; st,rbatt-mohm = <427>; st,rbatt-mohm = <477>; st,nom-capacity-mah = <450>; st,rsense-mohm = <25>; st,float-voltage-mv = <4200>; st,alarm-soc = <10>; st,alarm-voltage-mv = <3400>; st,term-current-ma = <50>; st,relax-current-ma = <25>; st,relax-current-ma = <100>; st,adaptive-capacity-tbl = /bits/ 16 <0x00 0x00 0x00 0x05 0x0d 0x2a 0x47>; st,ocv-tbl = Loading @@ -170,5 +170,9 @@ /bits/ 16 <0x55 0x5a 0x64 0xa0 0x140 0x1b8 0x348>; st,temp-tbl = /bits/ 16 <60 40 25 10 0 (-10) (-20)>; st,empty-soc-uv = <3300000>; qcom,fg-vadc = <&pm8916_vadc>; qcom,fg-adc_tm = <&pm8916_adc_tm>; }; };
drivers/power/stc3117_battery.c +189 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ #include <linux/slab.h> #include <linux/power_supply.h> #include <linux/qpnp/qpnp-adc.h> #define MODE_REG 0x0 #define VMODE_BIT BIT(0) #define FORCE_CD_BIT BIT(2) Loading Loading @@ -119,7 +121,7 @@ do { \ struct fg_data { int soc; int hr_soc; u16 voltage_mv; s16 voltage_mv; s16 current_ma; s16 avg_current_ma; s16 temperature; Loading Loading @@ -172,6 +174,7 @@ struct stc311x_chip { int cfg_rbatt_mohm; int cfg_term_current_ma; int cfg_float_voltage_mv; int cfg_empty_soc_uv; bool cfg_force_vmode; bool cfg_enable_soc_correction; Loading @@ -192,10 +195,17 @@ struct stc311x_chip { struct i2c_client *client; struct device *dev; struct delayed_work soc_calc_work; struct delayed_work cutoff_check_work; struct fg_wakeup_source soc_calc_wake_source; struct fg_wakeup_source cutoff_wake_source; struct dentry *debug_root; struct mutex read_write_lock; struct power_supply bms_psy; /* ADC notification */ struct qpnp_adc_tm_chip *adc_tm_dev; struct qpnp_vadc_chip *vadc_dev; struct qpnp_adc_tm_btm_param vbat_cutoff_params; }; static char *fg_supplicants[] = { Loading Loading @@ -658,11 +668,145 @@ static int stc311x_start(struct stc311x_chip *chip) return rc; } static int stc311x_force_soc(struct stc311x_chip *chip, int soc, int vbat_uv) { int rc; rc = stc311x_write_param(chip, SOC_REG, soc); if (rc) pr_err("Failed to set SOC to %d rc=%d\n", soc, rc); else pr_warn("forcing SOC = %d vbat = %duV\n", soc, vbat_uv); return rc; } static int get_battery_voltage_adc(struct stc311x_chip *chip, int *result_uv) { int rc; struct qpnp_vadc_result adc_result; if (!chip->vadc_dev) return -EINVAL; rc = qpnp_vadc_read(chip->vadc_dev, VBAT_SNS, &adc_result); if (rc) { pr_err("error reading adc channel = %d, rc = %d\n", VBAT_SNS, rc); return rc; } pr_debug("mvolts phy=%lld meas=0x%llx\n", adc_result.physical, adc_result.measurement); *result_uv = (int)adc_result.physical; return 0; } #define VBATT_ERROR_MARGIN_UV 20000 #define CUTOFF_MARGIN_UV 100000 #define CUTOFF_DELAY 30000 static void btm_notify_vbat(enum qpnp_tm_state state, void *ctx) { struct stc311x_chip *chip = ctx; int vbat_uv; int rc; rc = get_battery_voltage_adc(chip, &vbat_uv); if (rc) { pr_err("failed to read battery voltage rc=%d\n", rc); goto out; } pr_debug("vbat is at %duV\n", vbat_uv); if (state == ADC_TM_LOW_STATE) { pr_debug("low voltage btm notification triggered\n"); if (vbat_uv < (chip->vbat_cutoff_params.low_thr + VBATT_ERROR_MARGIN_UV)) { fg_stay_awake(&chip->cutoff_wake_source); schedule_delayed_work(&chip->cutoff_check_work, msecs_to_jiffies(CUTOFF_DELAY)); chip->vbat_cutoff_params.state_request = ADC_TM_HIGH_THR_ENABLE; } else { pr_debug("faulty btm trigger, discarding\n"); } } else if (state == ADC_TM_HIGH_STATE) { pr_debug("high voltage btm notification triggered\n"); if (vbat_uv >= chip->vbat_cutoff_params.high_thr) { chip->vbat_cutoff_params.state_request = ADC_TM_LOW_THR_ENABLE; cancel_delayed_work_sync(&chip->cutoff_check_work); fg_relax(&chip->cutoff_wake_source); } else { pr_debug("faulty btm trigger, discarding\n"); } } else { pr_debug("unknown voltage notification state: %d\n", state); } out: qpnp_adc_tm_channel_measure(chip->adc_tm_dev, &chip->vbat_cutoff_params); } static int stc311x_vbat_cutoff_monitor(struct stc311x_chip *chip) { int rc; chip->vbat_cutoff_params.low_thr = chip->cfg_empty_soc_uv + CUTOFF_MARGIN_UV; chip->vbat_cutoff_params.high_thr = chip->cfg_empty_soc_uv + CUTOFF_MARGIN_UV + VBATT_ERROR_MARGIN_UV; chip->vbat_cutoff_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE; chip->vbat_cutoff_params.channel = VBAT_SNS; chip->vbat_cutoff_params.btm_ctx = chip; chip->vbat_cutoff_params.timer_interval = ADC_MEAS1_INTERVAL_16S; chip->vbat_cutoff_params.threshold_notification = &btm_notify_vbat; rc = qpnp_adc_tm_channel_measure(chip->adc_tm_dev, &chip->vbat_cutoff_params); if (rc) { pr_err("adc-tm setup failed: %d\n", rc); return rc; } pr_debug("set low thr to %d and high to %d\n", chip->vbat_cutoff_params.low_thr, chip->vbat_cutoff_params.high_thr); return 0; } int stc311x_parse_dt(struct stc311x_chip *chip) { int rc = 0; int prop_len; chip->cfg_empty_soc_uv = -EINVAL; OF_PROP_READ(chip, chip->cfg_empty_soc_uv, "empty-soc-uv", rc, 1); if (chip->cfg_empty_soc_uv > 0) { chip->vadc_dev = qpnp_get_vadc(chip->dev, "fg"); if (IS_ERR(chip->vadc_dev)) { rc = PTR_ERR(chip->vadc_dev); if (rc != -EPROBE_DEFER) pr_err("vadc not found defer probe\n"); return rc; } chip->adc_tm_dev = qpnp_get_adc_tm(chip->dev, "fg"); if (IS_ERR(chip->adc_tm_dev)) { rc = PTR_ERR(chip->adc_tm_dev); if (rc != -EPROBE_DEFER) pr_err("adc-tm property missing, rc=%d\n", rc); return rc; } rc = stc311x_vbat_cutoff_monitor(chip); if (rc) pr_err("failed to configure vbat cutoff monitor rc=%d\n", rc); } OF_PROP_READ(chip, chip->cfg_rbatt_mohm, "rbatt-mohm", rc, 0); OF_PROP_READ(chip, chip->cfg_cnom_mah, Loading Loading @@ -1283,6 +1427,45 @@ static void soc_calc_work_fn(struct work_struct *work) schedule_delayed_work(&chip->soc_calc_work, msecs_to_jiffies(delay)); } static void cutoff_check_work_fn(struct work_struct *work) { int rc; int vbat_uv; struct stc311x_chip *chip = container_of(work, struct stc311x_chip, cutoff_check_work.work); rc = get_battery_voltage_adc(chip, &vbat_uv); if (rc) { pr_err("failed to read VBAT rc=%d\n", rc); goto out; } pr_debug("vbat is at %duV\n", vbat_uv); /* * release wakeup source and return if battery voltage crosses high * threhold limit. */ if (vbat_uv >= (chip->vbat_cutoff_params.high_thr + VBATT_ERROR_MARGIN_UV)) { fg_relax(&chip->cutoff_wake_source); return; } if (vbat_uv <= (chip->cfg_empty_soc_uv)) { rc = stc311x_force_soc(chip, 0, vbat_uv); if (rc) goto out; stc311x_process_fg_task(chip); return; } out: schedule_delayed_work(&chip->cutoff_check_work, msecs_to_jiffies(CUTOFF_DELAY)); } static enum power_supply_property stc311x_bms_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, Loading Loading @@ -1546,6 +1729,7 @@ static int stc311x_probe(struct i2c_client *client, chip->client = client; mutex_init(&chip->read_write_lock); INIT_DELAYED_WORK(&chip->soc_calc_work, soc_calc_work_fn); INIT_DELAYED_WORK(&chip->cutoff_check_work, cutoff_check_work_fn); /* read chip id */ rc = stc311x_read_raw(chip, ID_REG, ®, 1); Loading @@ -1568,6 +1752,9 @@ static int stc311x_probe(struct i2c_client *client, chip->soc_calc_wake_source.disabled = 1; wakeup_source_init(&chip->soc_calc_wake_source.source, "fg_soc_calc_wake"); chip->cutoff_wake_source.disabled = 1; wakeup_source_init(&chip->cutoff_wake_source.source, "fg_cutoff_wake"); rc = stc311x_start(chip); if (rc) { pr_err("failed to start rc=%d\n", rc); Loading Loading @@ -1597,6 +1784,7 @@ static int stc311x_probe(struct i2c_client *client, fail: wakeup_source_trash(&chip->soc_calc_wake_source.source); wakeup_source_trash(&chip->cutoff_wake_source.source); return rc; } Loading