From eba048d51838b24856588fd2b774a5d1c4de30c7 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Mon, 28 Nov 2016 18:05:20 -0800 Subject: [PATCH 1/2] qpnp-fg-gen3: add support to configure Rconn Add support to configure battery connector resistance (Rconn) so that battery resistance can take into account of that along with ESR and Rslow. Change-Id: Ib330c4f9b6d55c75aeb8e8fd5cea4dd92b0f64cf Signed-off-by: Subbaraman Narayanamurthy --- .../power/qcom-charger/qpnp-fg-gen3.txt | 7 ++ drivers/power/qcom-charger/fg-core.h | 3 +- drivers/power/qcom-charger/qpnp-fg-gen3.c | 101 +++++++++++++++++- 3 files changed, 106 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt index baf534695cc1..ac63b3c3bf01 100644 --- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt @@ -266,6 +266,13 @@ 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,fg-rconn-mohms + Usage: optional + Value type: + Definition: Battery connector resistance (Rconn) in milliohms. If Rconn + is specified, then ESR to Rslow scaling factors will be + updated to account it for an accurate ESR. + ========================================================== Second Level Nodes - Peripherals managed by FG Gen3 driver ========================================================== diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h index e284de8bdff1..d8b6754a465f 100644 --- a/drivers/power/qcom-charger/fg-core.h +++ b/drivers/power/qcom-charger/fg-core.h @@ -211,10 +211,10 @@ struct fg_dt_props { int recharge_soc_thr; int recharge_volt_thr_mv; int rsense_sel; - int jeita_thresholds[NUM_JEITA_LEVELS]; int esr_timer_charging; int esr_timer_awake; int esr_timer_asleep; + int rconn_mohms; int cl_start_soc; int cl_max_temp; int cl_min_temp; @@ -224,6 +224,7 @@ struct fg_dt_props { int cl_min_cap_limit; int jeita_hyst_temp; int batt_temp_delta; + int jeita_thresholds[NUM_JEITA_LEVELS]; int ki_coeff_soc[KI_COEFF_SOC_LEVELS]; int ki_coeff_med_dischg[KI_COEFF_SOC_LEVELS]; int ki_coeff_hi_dischg[KI_COEFF_SOC_LEVELS]; diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c index f1dc6d8ab2fb..b7b437c6313c 100644 --- a/drivers/power/qcom-charger/qpnp-fg-gen3.c +++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c @@ -62,6 +62,10 @@ #define ESR_TIMER_CHG_INIT_OFFSET 2 #define PROFILE_LOAD_WORD 24 #define PROFILE_LOAD_OFFSET 0 +#define ESR_RSLOW_DISCHG_WORD 34 +#define ESR_RSLOW_DISCHG_OFFSET 0 +#define ESR_RSLOW_CHG_WORD 51 +#define ESR_RSLOW_CHG_OFFSET 0 #define NOM_CAP_WORD 58 #define NOM_CAP_OFFSET 0 #define ACT_BATT_CAP_BKUP_WORD 74 @@ -69,6 +73,7 @@ #define CYCLE_COUNT_WORD 75 #define CYCLE_COUNT_OFFSET 0 #define PROFILE_INTEGRITY_WORD 79 +#define SW_CONFIG_OFFSET 0 #define PROFILE_INTEGRITY_OFFSET 3 #define BATT_SOC_WORD 91 #define BATT_SOC_OFFSET 0 @@ -564,21 +569,21 @@ static int fg_get_battery_esr(struct fg_chip *chip, int *val) static int fg_get_battery_resistance(struct fg_chip *chip, int *val) { - int rc, esr, rslow; + int rc, esr_uohms, rslow_uohms; - rc = fg_get_battery_esr(chip, &esr); + rc = fg_get_battery_esr(chip, &esr_uohms); if (rc < 0) { pr_err("failed to get ESR, rc=%d\n", rc); return rc; } - rc = fg_get_sram_prop(chip, FG_SRAM_RSLOW, &rslow); + rc = fg_get_sram_prop(chip, FG_SRAM_RSLOW, &rslow_uohms); if (rc < 0) { pr_err("failed to get Rslow, rc=%d\n", rc); return rc; } - *val = esr + rslow; + *val = esr_uohms + rslow_uohms; return 0; } @@ -1407,6 +1412,80 @@ static int fg_charge_full_update(struct fg_chip *chip) return 0; } +#define RCONN_CONFIG_BIT BIT(0) +static int fg_rconn_config(struct fg_chip *chip) +{ + int rc, esr_uohms; + u64 scaling_factor; + u32 val = 0; + + rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD, + SW_CONFIG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading SW_CONFIG_OFFSET, rc=%d\n", rc); + return rc; + } + + if (val & RCONN_CONFIG_BIT) { + fg_dbg(chip, FG_STATUS, "Rconn already configured: %x\n", val); + return 0; + } + + rc = fg_get_battery_esr(chip, &esr_uohms); + if (rc < 0) { + pr_err("failed to get ESR, rc=%d\n", rc); + return rc; + } + + scaling_factor = div64_u64((u64)esr_uohms * 1000, + esr_uohms + (chip->dt.rconn_mohms * 1000)); + + rc = fg_sram_read(chip, ESR_RSLOW_CHG_WORD, + ESR_RSLOW_CHG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading ESR_RSLOW_CHG_OFFSET, rc=%d\n", rc); + return rc; + } + + val *= scaling_factor; + do_div(val, 1000); + rc = fg_sram_write(chip, ESR_RSLOW_CHG_WORD, + ESR_RSLOW_CHG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing ESR_RSLOW_CHG_OFFSET, rc=%d\n", rc); + return rc; + } + fg_dbg(chip, FG_STATUS, "esr_rslow_chg modified to %x\n", val & 0xFF); + + rc = fg_sram_read(chip, ESR_RSLOW_DISCHG_WORD, + ESR_RSLOW_DISCHG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading ESR_RSLOW_DISCHG_OFFSET, rc=%d\n", rc); + return rc; + } + + val *= scaling_factor; + do_div(val, 1000); + rc = fg_sram_write(chip, ESR_RSLOW_DISCHG_WORD, + ESR_RSLOW_DISCHG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing ESR_RSLOW_DISCHG_OFFSET, rc=%d\n", rc); + return rc; + } + fg_dbg(chip, FG_STATUS, "esr_rslow_dischg modified to %x\n", + val & 0xFF); + + val = RCONN_CONFIG_BIT; + rc = fg_sram_write(chip, PROFILE_INTEGRITY_WORD, + SW_CONFIG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing SW_CONFIG_OFFSET, rc=%d\n", rc); + return rc; + } + + return 0; +} + static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc) { u8 buf[4]; @@ -2614,6 +2693,14 @@ static int fg_hw_init(struct fg_chip *chip) return rc; } + if (chip->dt.rconn_mohms > 0) { + rc = fg_rconn_config(chip); + if (rc < 0) { + pr_err("Error in configuring Rconn, rc=%d\n", rc); + return rc; + } + } + return 0; } @@ -3282,6 +3369,12 @@ static int fg_parse_dt(struct fg_chip *chip) if (rc < 0) pr_err("Error in parsing Ki coefficients, rc=%d\n", rc); + rc = of_property_read_u32(node, "qcom,fg-rconn-mohms", &temp); + if (rc < 0) + chip->dt.rconn_mohms = -EINVAL; + else + chip->dt.rconn_mohms = temp; + return 0; } From 658891b3b91f47fd768e99342db3b6afb05543c1 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Tue, 6 Dec 2016 14:41:29 -0800 Subject: [PATCH 2/2] qpnp-fg-gen3: fix a possible wake source count leak Currently, in the power supply notifier callback, GEN3 FG driver calls pm_stay_awake() which increases the wake source count on its device before it schedules status_change_work. In case, if status_change_work is already pending, then pm_stay_awake() will not have a balanced pm_relax() call as the work will not get scheduled again if it's pending already. Fix this by scheduling status_change_work and hence call pm_stay_awake() only if it is not pending. Change-Id: I2b8e28f6bfe1a5813410c7b1276d4e229eb67e84 Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/qcom-charger/qpnp-fg-gen3.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c index b7b437c6313c..fd470f62c2b0 100644 --- a/drivers/power/qcom-charger/qpnp-fg-gen3.c +++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c @@ -2448,6 +2448,9 @@ static int fg_notifier_cb(struct notifier_block *nb, if (event != PSY_EVENT_PROP_CHANGED) return NOTIFY_OK; + if (work_pending(&chip->status_change_work)) + return NOTIFY_OK; + if ((strcmp(psy->desc->name, "battery") == 0) || (strcmp(psy->desc->name, "usb") == 0)) { /*