diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt index 221657780178..8adfeebb1580 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt @@ -99,10 +99,10 @@ First Level Node - FG Gen3 device - qcom,fg-delta-soc-thr Usage: optional Value type: - Definition: Percentage of monotonic SOC increase upon which the delta - SOC interrupt will be triggered. If this property is not - specified, then the default value will be 1. Possible - values are in the range of 0 to 12. + Definition: Percentage of SOC increase upon which the delta monotonic & + battery SOC interrupts will be triggered. If this property + is not specified, then the default value will be 1. + Possible values are in the range of 0 to 12. - qcom,fg-recharge-soc-thr Usage: optional @@ -156,12 +156,12 @@ First Level Node - FG Gen3 device - qcom,cycle-counter-en Usage: optional - Value type: + Value type: Definition: Enables the cycle counter feature. - qcom,fg-force-load-profile Usage: optional - Value type: + Value type: Definition: If set, battery profile will be force loaded if the profile loaded earlier by bootloader doesn't match with the profile available in the device tree. @@ -229,13 +229,13 @@ 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: +- qcom,hold-soc-while-full Usage: optional - Value type: + Value type: Definition: A boolean property that when defined holds SOC at 100% when the battery is full. -- qcom,ki-coeff-soc-dischg: +- qcom,ki-coeff-soc-dischg Usage: optional Value type: Definition: Array of monotonic SOC threshold values to change the ki @@ -243,7 +243,7 @@ First Level Node - FG Gen3 device This should be defined in the ascending order and in the range of 0-100. Array limit is set to 3. -- qcom,ki-coeff-med-dischg: +- qcom,ki-coeff-med-dischg Usage: optional Value type: Definition: Array of ki coefficient values for medium discharge current @@ -254,7 +254,7 @@ First Level Node - FG Gen3 device is specified to make it fully functional. Value has no unit. Allowed range is 0 to 62200 in micro units. -- qcom,ki-coeff-hi-dischg: +- qcom,ki-coeff-hi-dischg Usage: optional Value type: Definition: Array of ki coefficient values for high discharge current @@ -311,6 +311,15 @@ First Level Node - FG Gen3 device 148438 (14.84 %) will be used. Lowest possible value is 1954 (0.19 %). +- qcom,fg-auto-recharge-soc + Usage: optional + Value type: + Definition: A boolean property when defined will configure automatic + recharge SOC threshold. If not specified, automatic + recharge voltage threshold will be configured. This has + to be configured in conjunction with the charger side + configuration for proper functionality. + ========================================================== Second Level Nodes - Peripherals managed by FG Gen3 driver ========================================================== diff --git a/arch/arm/boot/dts/qcom/msm-pm660.dtsi b/arch/arm/boot/dts/qcom/msm-pm660.dtsi index f0624d2572eb..1154c28bb9ea 100644 --- a/arch/arm/boot/dts/qcom/msm-pm660.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm660.dtsi @@ -518,8 +518,10 @@ reg = <0x4000 0x100>; interrupts = <0x0 0x40 0x0 IRQ_TYPE_EDGE_BOTH>, <0x0 0x40 0x1 IRQ_TYPE_EDGE_BOTH>, - <0x0 0x40 0x2 IRQ_TYPE_EDGE_BOTH>, - <0x0 0x40 0x3 IRQ_TYPE_EDGE_BOTH>, + <0x0 0x40 0x2 + IRQ_TYPE_EDGE_RISING>, + <0x0 0x40 0x3 + IRQ_TYPE_EDGE_RISING>, <0x0 0x40 0x4 IRQ_TYPE_EDGE_BOTH>, <0x0 0x40 0x5 IRQ_TYPE_EDGE_RISING>, diff --git a/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi b/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi index be47b6483288..9218f8dc583c 100644 --- a/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi @@ -333,8 +333,10 @@ reg = <0x4000 0x100>; interrupts = <0x2 0x40 0x0 IRQ_TYPE_EDGE_BOTH>, <0x2 0x40 0x1 IRQ_TYPE_EDGE_BOTH>, - <0x2 0x40 0x2 IRQ_TYPE_EDGE_BOTH>, - <0x2 0x40 0x3 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x2 + IRQ_TYPE_EDGE_RISING>, + <0x2 0x40 0x3 + IRQ_TYPE_EDGE_RISING>, <0x2 0x40 0x4 IRQ_TYPE_EDGE_BOTH>, <0x2 0x40 0x5 IRQ_TYPE_EDGE_RISING>, diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index d71e3afd8d42..c0d30b96122c 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -156,7 +156,8 @@ enum fg_sram_param_id { FG_SRAM_ESR_TIMER_CHG_INIT, FG_SRAM_SYS_TERM_CURR, FG_SRAM_CHG_TERM_CURR, - FG_SRAM_DELTA_SOC_THR, + FG_SRAM_DELTA_MSOC_THR, + FG_SRAM_DELTA_BSOC_THR, FG_SRAM_RECHARGE_SOC_THR, FG_SRAM_RECHARGE_VBATT_THR, FG_SRAM_KI_COEFF_MED_DISCHG, @@ -205,6 +206,7 @@ enum wa_flags { struct fg_dt_props { bool force_load_profile; bool hold_soc_while_full; + bool auto_recharge_soc; int cutoff_volt_mv; int empty_volt_mv; int vbatt_low_thr_mv; @@ -345,6 +347,7 @@ struct fg_chip { bool esr_fcc_ctrl_en; bool soc_reporting_ready; bool esr_flt_cold_temp_en; + bool bsoc_delta_irq_en; struct completion soc_update; struct completion soc_ready; struct delayed_work profile_load_work; diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index 744cbde7fc7f..92c8618ec440 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -48,8 +48,10 @@ #define KI_COEFF_HI_DISCHG_OFFSET 0 #define KI_COEFF_LOW_DISCHG_WORD 10 #define KI_COEFF_LOW_DISCHG_OFFSET 2 -#define DELTA_SOC_THR_WORD 12 -#define DELTA_SOC_THR_OFFSET 3 +#define DELTA_MSOC_THR_WORD 12 +#define DELTA_MSOC_THR_OFFSET 3 +#define DELTA_BSOC_THR_WORD 13 +#define DELTA_BSOC_THR_OFFSET 2 #define RECHARGE_SOC_THR_WORD 14 #define RECHARGE_SOC_THR_OFFSET 0 #define CHG_TERM_CURR_WORD 14 @@ -113,8 +115,10 @@ #define KI_COEFF_MED_DISCHG_v2_OFFSET 0 #define KI_COEFF_HI_DISCHG_v2_WORD 10 #define KI_COEFF_HI_DISCHG_v2_OFFSET 1 -#define DELTA_SOC_THR_v2_WORD 13 -#define DELTA_SOC_THR_v2_OFFSET 0 +#define DELTA_BSOC_THR_v2_WORD 12 +#define DELTA_BSOC_THR_v2_OFFSET 3 +#define DELTA_MSOC_THR_v2_WORD 13 +#define DELTA_MSOC_THR_v2_OFFSET 0 #define RECHARGE_SOC_THR_v2_WORD 14 #define RECHARGE_SOC_THR_v2_OFFSET 1 #define CHG_TERM_CURR_v2_WORD 15 @@ -143,6 +147,8 @@ static void fg_encode_current(struct fg_sram_param *sp, static void fg_encode_default(struct fg_sram_param *sp, enum fg_sram_param_id id, int val, u8 *buf); +static struct fg_irq_info fg_irqs[FG_IRQ_MAX]; + #define PARAM(_id, _addr_word, _addr_byte, _len, _num, _den, _offset, \ _enc, _dec) \ [FG_SRAM_##_id] = { \ @@ -188,8 +194,10 @@ static struct fg_sram_param pmi8998_v1_sram_params[] = { 1000000, 122070, 0, fg_encode_current, NULL), PARAM(CHG_TERM_CURR, CHG_TERM_CURR_WORD, CHG_TERM_CURR_OFFSET, 1, 100000, 390625, 0, fg_encode_current, NULL), - PARAM(DELTA_SOC_THR, DELTA_SOC_THR_WORD, DELTA_SOC_THR_OFFSET, 1, 2048, - 100, 0, fg_encode_default, NULL), + PARAM(DELTA_MSOC_THR, DELTA_MSOC_THR_WORD, DELTA_MSOC_THR_OFFSET, 1, + 2048, 100, 0, fg_encode_default, NULL), + PARAM(DELTA_BSOC_THR, DELTA_BSOC_THR_WORD, DELTA_BSOC_THR_OFFSET, 1, + 2048, 100, 0, fg_encode_default, NULL), PARAM(RECHARGE_SOC_THR, RECHARGE_SOC_THR_WORD, RECHARGE_SOC_THR_OFFSET, 1, 256, 100, 0, fg_encode_default, NULL), PARAM(ESR_TIMER_DISCHG_MAX, ESR_TIMER_DISCHG_MAX_WORD, @@ -248,8 +256,10 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = { 1000000, 122070, 0, fg_encode_current, NULL), PARAM(CHG_TERM_CURR, CHG_TERM_CURR_v2_WORD, CHG_TERM_CURR_v2_OFFSET, 1, 100000, 390625, 0, fg_encode_current, NULL), - PARAM(DELTA_SOC_THR, DELTA_SOC_THR_v2_WORD, DELTA_SOC_THR_v2_OFFSET, 1, - 2048, 100, 0, fg_encode_default, NULL), + PARAM(DELTA_MSOC_THR, DELTA_MSOC_THR_v2_WORD, DELTA_MSOC_THR_v2_OFFSET, + 1, 2048, 100, 0, fg_encode_default, NULL), + PARAM(DELTA_BSOC_THR, DELTA_BSOC_THR_v2_WORD, DELTA_BSOC_THR_v2_OFFSET, + 1, 2048, 100, 0, fg_encode_default, NULL), PARAM(RECHARGE_SOC_THR, RECHARGE_SOC_THR_v2_WORD, RECHARGE_SOC_THR_v2_OFFSET, 1, 256, 100, 0, fg_encode_default, NULL), @@ -773,6 +783,7 @@ static bool is_debug_batt_id(struct fg_chip *chip) #define FULL_CAPACITY 100 #define FULL_SOC_RAW 255 #define DEBUG_BATT_SOC 67 +#define BATT_MISS_SOC 50 #define EMPTY_SOC 0 static int fg_get_prop_capacity(struct fg_chip *chip, int *val) { @@ -783,6 +794,16 @@ static int fg_get_prop_capacity(struct fg_chip *chip, int *val) return 0; } + if (chip->fg_restarting) { + *val = chip->last_soc; + return 0; + } + + if (chip->battery_missing) { + *val = BATT_MISS_SOC; + return 0; + } + if (is_batt_empty(chip)) { *val = EMPTY_SOC; return 0; @@ -1386,6 +1407,36 @@ static int fg_adjust_ki_coeff_dischg(struct fg_chip *chip) return 0; } +static int fg_set_recharge_voltage(struct fg_chip *chip, int voltage_mv) +{ + u8 buf; + int rc; + + if (chip->dt.auto_recharge_soc) + return 0; + + /* This configuration is available only for pmicobalt v2.0 and above */ + if (chip->wa_flags & PMI8998_V1_REV_WA) + return 0; + + fg_dbg(chip, FG_STATUS, "Setting recharge voltage to %dmV\n", + voltage_mv); + fg_encode(chip->sp, FG_SRAM_RECHARGE_VBATT_THR, voltage_mv, &buf); + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_RECHARGE_VBATT_THR].addr_word, + chip->sp[FG_SRAM_RECHARGE_VBATT_THR].addr_byte, + &buf, chip->sp[FG_SRAM_RECHARGE_VBATT_THR].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing recharge_vbatt_thr, rc=%d\n", + rc); + return rc; + } + + return 0; +} + +#define AUTO_RECHG_VOLT_LOW_LIMIT_MV 3700 static int fg_charge_full_update(struct fg_chip *chip) { union power_supply_propval prop = {0, }; @@ -1398,6 +1449,16 @@ static int fg_charge_full_update(struct fg_chip *chip) if (!batt_psy_initialized(chip)) return 0; + if (!chip->charge_done && chip->bsoc_delta_irq_en) { + disable_irq_wake(fg_irqs[BSOC_DELTA_IRQ].irq); + disable_irq_nosync(fg_irqs[BSOC_DELTA_IRQ].irq); + chip->bsoc_delta_irq_en = false; + } else if (chip->charge_done && !chip->bsoc_delta_irq_en) { + enable_irq(fg_irqs[BSOC_DELTA_IRQ].irq); + enable_irq_wake(fg_irqs[BSOC_DELTA_IRQ].irq); + chip->bsoc_delta_irq_en = true; + } + rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_HEALTH, &prop); if (rc < 0) { @@ -1423,18 +1484,46 @@ static int fg_charge_full_update(struct fg_chip *chip) return rc; } - fg_dbg(chip, FG_STATUS, "msoc: %d health: %d status: %d\n", msoc, - chip->health, chip->charge_status); - if (chip->charge_done) { - if (msoc >= 99 && chip->health == POWER_SUPPLY_HEALTH_GOOD) + fg_dbg(chip, FG_STATUS, "msoc: %d bsoc: %x health: %d status: %d full: %d\n", + msoc, bsoc, chip->health, chip->charge_status, + chip->charge_full); + if (chip->charge_done && !chip->charge_full) { + if (msoc >= 99 && chip->health == POWER_SUPPLY_HEALTH_GOOD) { + fg_dbg(chip, FG_STATUS, "Setting charge_full to true\n"); chip->charge_full = true; - else + /* + * Lower the recharge voltage so that VBAT_LT_RECHG + * signal will not be asserted soon. + */ + rc = fg_set_recharge_voltage(chip, + AUTO_RECHG_VOLT_LOW_LIMIT_MV); + if (rc < 0) { + pr_err("Error in reducing recharge voltage, rc=%d\n", + rc); + return rc; + } + } else { fg_dbg(chip, FG_STATUS, "Terminated charging @ SOC%d\n", msoc); - } else if ((bsoc >> 8) <= recharge_soc) { + } + } else if ((bsoc >> 8) <= recharge_soc && chip->charge_full) { fg_dbg(chip, FG_STATUS, "bsoc: %d recharge_soc: %d\n", bsoc >> 8, recharge_soc); chip->charge_full = false; + /* + * Raise the recharge voltage so that VBAT_LT_RECHG signal + * will be asserted soon as battery SOC had dropped below + * the recharge SOC threshold. + */ + rc = fg_set_recharge_voltage(chip, + chip->dt.recharge_volt_thr_mv); + if (rc < 0) { + pr_err("Error in setting recharge voltage, rc=%d\n", + rc); + return rc; + } + } else { + return 0; } if (!chip->charge_full) @@ -1539,13 +1628,16 @@ static int fg_rconn_config(struct fg_chip *chip) static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc) { - u8 buf[4]; + u8 buf; int rc; - fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, recharge_soc, buf); + if (!chip->dt.auto_recharge_soc) + return 0; + + 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].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); @@ -1559,6 +1651,9 @@ static int fg_adjust_recharge_soc(struct fg_chip *chip) { int rc, msoc, recharge_soc, new_recharge_soc = 0; + if (!chip->dt.auto_recharge_soc) + return 0; + recharge_soc = chip->dt.recharge_soc_thr; /* * If the input is present and charging had been terminated, adjust @@ -2062,7 +2157,6 @@ wait: goto wait; } else if (rc <= 0) { pr_err("wait for soc_ready timed out rc=%d\n", rc); - goto out; } rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); @@ -2494,13 +2588,13 @@ static int fg_psy_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_CAPACITY: - if (chip->fg_restarting) - pval->intval = chip->last_soc; - else - rc = fg_get_prop_capacity(chip, &pval->intval); + rc = fg_get_prop_capacity(chip, &pval->intval); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - rc = fg_get_battery_voltage(chip, &pval->intval); + if (chip->battery_missing) + pval->intval = 3700000; + else + rc = fg_get_battery_voltage(chip, &pval->intval); break; case POWER_SUPPLY_PROP_CURRENT_NOW: rc = fg_get_battery_current(chip, &pval->intval); @@ -2753,15 +2847,27 @@ static int fg_hw_init(struct fg_chip *chip) } if (chip->dt.delta_soc_thr > 0 && chip->dt.delta_soc_thr < 100) { - fg_encode(chip->sp, FG_SRAM_DELTA_SOC_THR, + fg_encode(chip->sp, FG_SRAM_DELTA_MSOC_THR, chip->dt.delta_soc_thr, buf); rc = fg_sram_write(chip, - chip->sp[FG_SRAM_DELTA_SOC_THR].addr_word, - chip->sp[FG_SRAM_DELTA_SOC_THR].addr_byte, - buf, chip->sp[FG_SRAM_DELTA_SOC_THR].len, + chip->sp[FG_SRAM_DELTA_MSOC_THR].addr_word, + chip->sp[FG_SRAM_DELTA_MSOC_THR].addr_byte, + buf, chip->sp[FG_SRAM_DELTA_MSOC_THR].len, FG_IMA_DEFAULT); if (rc < 0) { - pr_err("Error in writing delta_soc_thr, rc=%d\n", rc); + pr_err("Error in writing delta_msoc_thr, rc=%d\n", rc); + return rc; + } + + fg_encode(chip->sp, FG_SRAM_DELTA_BSOC_THR, + chip->dt.delta_soc_thr, buf); + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_DELTA_BSOC_THR].addr_word, + chip->sp[FG_SRAM_DELTA_BSOC_THR].addr_byte, + buf, chip->sp[FG_SRAM_DELTA_BSOC_THR].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing delta_bsoc_thr, rc=%d\n", rc); return rc; } } @@ -2774,18 +2880,11 @@ static int fg_hw_init(struct fg_chip *chip) } } - /* This configuration is available only for pmicobalt v2.0 and above */ - if (!(chip->wa_flags & PMI8998_V1_REV_WA) && - chip->dt.recharge_volt_thr_mv > 0) { - fg_encode(chip->sp, FG_SRAM_RECHARGE_VBATT_THR, - chip->dt.recharge_volt_thr_mv, buf); - rc = fg_sram_write(chip, - chip->sp[FG_SRAM_RECHARGE_VBATT_THR].addr_word, - chip->sp[FG_SRAM_RECHARGE_VBATT_THR].addr_byte, - buf, chip->sp[FG_SRAM_RECHARGE_VBATT_THR].len, - FG_IMA_DEFAULT); + if (chip->dt.recharge_volt_thr_mv > 0) { + rc = fg_set_recharge_voltage(chip, + chip->dt.recharge_volt_thr_mv); if (rc < 0) { - pr_err("Error in writing recharge_vbatt_thr, rc=%d\n", + pr_err("Error in setting recharge_voltage, rc=%d\n", rc); return rc; } @@ -3052,7 +3151,20 @@ static irqreturn_t fg_soc_update_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data) +static irqreturn_t fg_delta_bsoc_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + int rc; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + rc = fg_charge_full_update(chip); + if (rc < 0) + pr_err("Error in charge_full_update, rc=%d\n", rc); + + return IRQ_HANDLED; +} + +static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) { struct fg_chip *chip = data; int rc; @@ -3126,12 +3238,13 @@ static struct fg_irq_info fg_irqs[FG_IRQ_MAX] = { }, [MSOC_DELTA_IRQ] = { .name = "msoc-delta", - .handler = fg_delta_soc_irq_handler, + .handler = fg_delta_msoc_irq_handler, .wakeable = true, }, [BSOC_DELTA_IRQ] = { .name = "bsoc-delta", - .handler = fg_dummy_irq_handler, + .handler = fg_delta_bsoc_irq_handler, + .wakeable = true, }, [SOC_READY_IRQ] = { .name = "soc-ready", @@ -3489,6 +3602,9 @@ static int fg_parse_dt(struct fg_chip *chip) else chip->dt.recharge_volt_thr_mv = temp; + chip->dt.auto_recharge_soc = of_property_read_bool(node, + "qcom,fg-auto-recharge-soc"); + rc = of_property_read_u32(node, "qcom,fg-rsense-sel", &temp); if (rc < 0) chip->dt.rsense_sel = SRC_SEL_BATFET_SMB; @@ -3746,6 +3862,13 @@ static int fg_gen3_probe(struct platform_device *pdev) if (fg_irqs[SOC_UPDATE_IRQ].irq) disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq); + /* Keep BSOC_DELTA_IRQ irq disabled until we require it */ + if (fg_irqs[BSOC_DELTA_IRQ].irq) { + disable_irq_wake(fg_irqs[BSOC_DELTA_IRQ].irq); + disable_irq_nosync(fg_irqs[BSOC_DELTA_IRQ].irq); + chip->bsoc_delta_irq_en = false; + } + rc = fg_debugfs_create(chip); if (rc < 0) { dev_err(chip->dev, "Error in creating debugfs entries, rc:%d\n",