From 5870397614d84786b608f23bfe87952a24441af2 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Mon, 19 Sep 2016 14:52:12 -0700 Subject: [PATCH 1/5] power: qpnp-fg-gen3: add support to configure VBtGTFltTh and VBtFull Based on the battery profile parameters, Vbatt greater than float voltage and full voltage thresholds needs to be configured. Add support for the same. Since this needs the battery profile parameters to be read in advance before the hardware is initialized, shuffle the code to get battery profile early. CRs-Fixed: 1068593 Change-Id: If5df62d933cc653a276967b3d16c080c6d4ecb2e Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/qcom-charger/fg-core.h | 10 +- drivers/power/qcom-charger/qpnp-fg-gen3.c | 114 +++++++++++++--------- 2 files changed, 74 insertions(+), 50 deletions(-) diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h index 515f31a44ce7..7c6dd45ae412 100644 --- a/drivers/power/qcom-charger/fg-core.h +++ b/drivers/power/qcom-charger/fg-core.h @@ -54,6 +54,8 @@ CHARS_PER_ITEM) + 1) \ #define FG_SRAM_ADDRESS_MAX 255 +#define PROFILE_LEN 224 +#define PROFILE_COMP_LEN 148 #define BUCKET_COUNT 8 #define BUCKET_SOC_PCT (256 / BUCKET_COUNT) @@ -121,6 +123,8 @@ enum fg_sram_param_id { FG_SRAM_CUTOFF_VOLT, FG_SRAM_EMPTY_VOLT, FG_SRAM_VBATT_LOW, + FG_SRAM_FLOAT_VOLT, + FG_SRAM_VBATT_FULL, FG_SRAM_ESR_TIMER_DISCHG_MAX, FG_SRAM_ESR_TIMER_DISCHG_INIT, FG_SRAM_ESR_TIMER_CHG_MAX, @@ -184,6 +188,7 @@ struct fg_batt_props { const char *batt_type_str; char *batt_profile; int float_volt_uv; + int vbatt_full_mv; int fastchg_curr_ma; int batt_id_kohm; }; @@ -217,7 +222,7 @@ struct fg_chip { struct votable *awake_votable; struct fg_sram_param *sp; int *debug_mask; - char *batt_profile; + char batt_profile[PROFILE_LEN]; struct fg_dt_props dt; struct fg_batt_props bp; struct fg_cyc_ctr_data cyc_ctr; @@ -227,10 +232,11 @@ struct fg_chip { u32 batt_soc_base; u32 batt_info_base; u32 mem_if_base; + int batt_id; int nom_cap_uah; int status; int prev_status; - bool batt_id_avail; + bool profile_available; bool profile_loaded; bool battery_missing; struct completion soc_update; diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c index 7739952f3254..f5b095115dcd 100644 --- a/drivers/power/qcom-charger/qpnp-fg-gen3.c +++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c @@ -35,6 +35,8 @@ #define CUTOFF_VOLT_OFFSET 0 #define SYS_TERM_CURR_WORD 6 #define SYS_TERM_CURR_OFFSET 0 +#define VBATT_FULL_WORD 7 +#define VBATT_FULL_OFFSET 0 #define DELTA_SOC_THR_WORD 12 #define DELTA_SOC_THR_OFFSET 3 #define RECHARGE_SOC_THR_WORD 14 @@ -89,6 +91,8 @@ #define EMPTY_VOLT_v2_OFFSET 3 #define VBATT_LOW_v2_WORD 16 #define VBATT_LOW_v2_OFFSET 0 +#define FLOAT_VOLT_v2_WORD 16 +#define FLOAT_VOLT_v2_OFFSET 2 static int fg_decode_value_16b(struct fg_sram_param *sp, enum fg_sram_param_id id, int val); @@ -97,9 +101,9 @@ static int fg_decode_default(struct fg_sram_param *sp, static int fg_decode_batt_soc(struct fg_sram_param *sp, enum fg_sram_param_id id, int val); static void fg_encode_voltage(struct fg_sram_param *sp, - enum fg_sram_param_id id, int val, u8 *buf); + enum fg_sram_param_id id, int val_mv, u8 *buf); static void fg_encode_current(struct fg_sram_param *sp, - enum fg_sram_param_id id, int val, u8 *buf); + enum fg_sram_param_id id, int val_ma, u8 *buf); static void fg_encode_default(struct fg_sram_param *sp, enum fg_sram_param_id id, int val, u8 *buf); @@ -134,6 +138,8 @@ static struct fg_sram_param pmicobalt_v1_sram_params[] = { -2500, fg_encode_voltage, NULL), PARAM(VBATT_LOW, VBATT_LOW_WORD, VBATT_LOW_OFFSET, 1, 100000, 390625, -2500, fg_encode_voltage, NULL), + PARAM(VBATT_FULL, VBATT_FULL_WORD, VBATT_FULL_OFFSET, 2, 1000000, + 244141, 0, fg_encode_voltage, NULL), PARAM(SYS_TERM_CURR, SYS_TERM_CURR_WORD, SYS_TERM_CURR_OFFSET, 3, 1000000, 122070, 0, fg_encode_current, NULL), PARAM(CHG_TERM_CURR, CHG_TERM_CURR_WORD, CHG_TERM_CURR_OFFSET, 1, @@ -172,6 +178,10 @@ static struct fg_sram_param pmicobalt_v2_sram_params[] = { 15625, -2000, fg_encode_voltage, NULL), PARAM(VBATT_LOW, VBATT_LOW_v2_WORD, VBATT_LOW_v2_OFFSET, 1, 1000, 15625, -2000, fg_encode_voltage, NULL), + PARAM(FLOAT_VOLT, FLOAT_VOLT_v2_WORD, FLOAT_VOLT_v2_OFFSET, 1, 1000, + 15625, -2000, fg_encode_voltage, NULL), + PARAM(VBATT_FULL, VBATT_FULL_WORD, VBATT_FULL_OFFSET, 2, 1000000, + 244141, 0, fg_encode_voltage, NULL), PARAM(SYS_TERM_CURR, SYS_TERM_CURR_WORD, SYS_TERM_CURR_OFFSET, 3, 1000000, 122070, 0, fg_encode_current, NULL), PARAM(CHG_TERM_CURR, CHG_TERM_CURR_v2_WORD, CHG_TERM_CURR_v2_OFFSET, 1, @@ -302,14 +312,14 @@ static int fg_decode(struct fg_sram_param *sp, enum fg_sram_param_id id, } static void fg_encode_voltage(struct fg_sram_param *sp, - enum fg_sram_param_id id, int val, u8 *buf) + enum fg_sram_param_id id, int val_mv, u8 *buf) { int i, mask = 0xff; int64_t temp; - val += sp[id].offset; - temp = (int64_t)div_u64((u64)val * sp[id].numrtr, sp[id].denmtr); - pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); + val_mv += sp[id].offset; + temp = (int64_t)div_u64((u64)val_mv * sp[id].numrtr, sp[id].denmtr); + pr_debug("temp: %llx id: %d, val_mv: %d, buf: [ ", temp, id, val_mv); for (i = 0; i < sp[id].len; i++) { buf[i] = temp & mask; temp >>= 8; @@ -319,15 +329,15 @@ static void fg_encode_voltage(struct fg_sram_param *sp, } static void fg_encode_current(struct fg_sram_param *sp, - enum fg_sram_param_id id, int val, u8 *buf) + enum fg_sram_param_id id, int val_ma, u8 *buf) { int i, mask = 0xff; int64_t temp; s64 current_ma; - current_ma = val; + current_ma = val_ma; temp = (int64_t)div_s64(current_ma * sp[id].numrtr, sp[id].denmtr); - pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); + pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val_ma); for (i = 0; i < sp[id].len; i++) { buf[i] = temp & mask; temp >>= 8; @@ -593,14 +603,12 @@ static int fg_get_batt_id(struct fg_chip *chip, int *val) return rc; } - chip->batt_id_avail = true; fg_dbg(chip, FG_STATUS, "batt_id: %d\n", batt_id); *val = batt_id; return 0; } -#define PROFILE_LEN 224 static int fg_get_batt_profile(struct fg_chip *chip) { struct device_node *node = chip->dev->of_node; @@ -614,13 +622,14 @@ static int fg_get_batt_profile(struct fg_chip *chip) return rc; } + batt_id /= 1000; + chip->batt_id = batt_id; batt_node = of_find_node_by_name(node, "qcom,battery-data"); if (!batt_node) { pr_err("Batterydata not available\n"); return -ENXIO; } - batt_id /= 1000; profile_node = of_batterydata_get_best_profile(batt_node, batt_id, NULL); if (IS_ERR(profile_node)) @@ -652,6 +661,13 @@ static int fg_get_batt_profile(struct fg_chip *chip) chip->bp.fastchg_curr_ma = -EINVAL; } + rc = of_property_read_u32(profile_node, "qcom,fg-cc-cv-threshold-mv", + &chip->bp.vbatt_full_mv); + if (rc < 0) { + pr_err("battery cc_cv threshold unavailable, rc:%d\n", rc); + chip->bp.vbatt_full_mv = -EINVAL; + } + data = of_get_property(profile_node, "qcom,fg-profile-data", &len); if (!data) { pr_err("No profile data available\n"); @@ -663,6 +679,7 @@ static int fg_get_batt_profile(struct fg_chip *chip) return -EINVAL; } + chip->profile_available = true; memcpy(chip->batt_profile, data, len); return 0; } @@ -912,7 +929,6 @@ static int fg_get_cycle_count(struct fg_chip *chip) return count; } -#define PROFILE_COMP_LEN 32 #define SOC_READY_WAIT_MS 2000 static void profile_load_work(struct work_struct *work) { @@ -923,11 +939,6 @@ static void profile_load_work(struct work_struct *work) u8 buf[PROFILE_COMP_LEN], val; bool tried_again = false, profiles_same = false; - if (!chip->batt_id_avail) { - pr_err("batt_id not available\n"); - return; - } - rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD, PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); if (rc < 0) { @@ -1182,6 +1193,32 @@ static int fg_hw_init(struct fg_chip *chip) return rc; } + /* This SRAM register is only present in v2.0 */ + if (chip->pmic_rev_id->rev4 == PMICOBALT_V2P0_REV4 && + chip->bp.float_volt_uv > 0) { + fg_encode(chip->sp, FG_SRAM_FLOAT_VOLT, + chip->bp.float_volt_uv / 1000, buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_FLOAT_VOLT].addr_word, + chip->sp[FG_SRAM_FLOAT_VOLT].addr_byte, buf, + chip->sp[FG_SRAM_FLOAT_VOLT].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing float_volt, rc=%d\n", rc); + return rc; + } + } + + if (chip->bp.vbatt_full_mv > 0) { + fg_encode(chip->sp, FG_SRAM_VBATT_FULL, chip->bp.vbatt_full_mv, + buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_VBATT_FULL].addr_word, + chip->sp[FG_SRAM_VBATT_FULL].addr_byte, buf, + chip->sp[FG_SRAM_VBATT_FULL].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing vbatt_full, rc=%d\n", rc); + return rc; + } + } + fg_encode(chip->sp, FG_SRAM_CHG_TERM_CURR, chip->dt.chg_term_curr_ma, buf); rc = fg_sram_write(chip, chip->sp[FG_SRAM_CHG_TERM_CURR].addr_word, @@ -1311,28 +1348,6 @@ static int fg_memif_init(struct fg_chip *chip) return fg_ima_init(chip); } -static int fg_batt_profile_init(struct fg_chip *chip) -{ - int rc; - - if (!chip->batt_profile) { - chip->batt_profile = devm_kcalloc(chip->dev, PROFILE_LEN, - sizeof(*chip->batt_profile), - GFP_KERNEL); - if (!chip->batt_profile) - return -ENOMEM; - } - - rc = fg_get_batt_profile(chip); - if (rc < 0) { - pr_err("Error in getting battery profile, rc:%d\n", rc); - return rc; - } - - schedule_delayed_work(&chip->profile_load_work, msecs_to_jiffies(0)); - return 0; -} - /* INTERRUPT HANDLERS STAY HERE */ static irqreturn_t fg_vbatt_low_irq_handler(int irq, void *data) @@ -1360,16 +1375,16 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data) chip->battery_missing = (status & BT_MISS_BIT); if (chip->battery_missing) { - chip->batt_id_avail = false; + chip->profile_available = false; chip->profile_loaded = false; clear_cycle_counter(chip); } else { - rc = fg_batt_profile_init(chip); + rc = fg_get_batt_profile(chip); if (rc < 0) { - pr_err("Error in initializing battery profile, rc=%d\n", - rc); + pr_err("Error in getting battery profile, rc:%d\n", rc); return IRQ_HANDLED; } + schedule_delayed_work(&chip->profile_load_work, 0); } return IRQ_HANDLED; @@ -1638,6 +1653,11 @@ static int fg_parse_dt(struct fg_chip *chip) } } + rc = fg_get_batt_profile(chip); + if (rc < 0) + pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n", + chip->batt_id, rc); + /* Read all the optional properties below */ rc = of_property_read_u32(node, "qcom,fg-cutoff-voltage", &temp); if (rc < 0) @@ -1836,10 +1856,8 @@ static int fg_gen3_probe(struct platform_device *pdev) goto exit; } - rc = fg_batt_profile_init(chip); - if (rc < 0) - dev_warn(chip->dev, "Error in initializing battery profile, rc:%d\n", - rc); + if (chip->profile_available) + schedule_delayed_work(&chip->profile_load_work, 0); device_init_wakeup(chip->dev, true); pr_debug("FG GEN3 driver successfully probed\n"); From 16b979380cbe71cd7f4169c2085e43e8db8e0701 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Mon, 19 Sep 2016 16:22:30 -0700 Subject: [PATCH 2/5] ARM: dts: msm: add CC_CV threshold to itech/ascent FG_GEN3 battery profiles Configure CC_CV threshold voltage to 4.34V for both itech and ascent battery profiles that will be used by GEN3 fuel gauge on pmicobalt. CRs-Fixed: 1068593 Change-Id: I7df3d343afa62ffdbf6f5db8c7fc7d94e2f0e3a0 Signed-off-by: Subbaraman Narayanamurthy --- arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi | 1 + arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi index 90df1d0c1ac0..69067f5f1cc7 100644 --- a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi @@ -13,6 +13,7 @@ qcom,ascent_3450mah { /* #Ascent_860_82209_0000_3450mAh_averaged_MasterSlave_Jul20th2016*/ qcom,max-voltage-uv = <4350000>; + qcom,fg-cc-cv-threshold-mv = <4340>; qcom,nom-batt-capacity-mah = <3450>; qcom,batt-id-kohm = <60>; qcom,battery-beta = <3435>; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi index 2c1edde56d6a..c3f23b75fa9c 100644 --- a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi @@ -13,6 +13,7 @@ qcom,itech_3000mah { /* #Itech_B00826LF_3000mAh_ver1660_averaged_MasterSlave_Jul20th2016*/ qcom,max-voltage-uv = <4350000>; + qcom,fg-cc-cv-threshold-mv = <4340>; qcom,nom-batt-capacity-mah = <3000>; qcom,batt-id-kohm = <100>; qcom,battery-beta = <3450>; From 5b6c4f8f1b3a0e4f838aa4c833e4655aaf81f37e Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Thu, 22 Sep 2016 18:11:42 -0700 Subject: [PATCH 3/5] qpnp-fg-gen3: change the encoding for delta soc threshold Currently, delta SOC threshold is written as a 8 bit value but the encoding is in 11 bits. This will cause the delta SOC threshold programmed to a lower value causing interrupts to fire quickly. Change the numerator of delta SOC SRAM parameter so that the delta SOC threshold will be set correctly. Maximum possible threshold will be 12.45% (1/2048 * 255). Change-Id: Ia908c917d71939b9d72b16ddca3400a692540bf3 Signed-off-by: Subbaraman Narayanamurthy --- .../devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt | 3 ++- drivers/power/qcom-charger/qpnp-fg-gen3.c | 4 ++-- 2 files changed, 4 insertions(+), 3 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 bd358593fcb3..9fa55eb0ce0d 100644 --- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt @@ -87,7 +87,8 @@ First Level Node - FG Gen3 device 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. + 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 diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c index f5b095115dcd..5545dd17cdd8 100644 --- a/drivers/power/qcom-charger/qpnp-fg-gen3.c +++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c @@ -144,7 +144,7 @@ static struct fg_sram_param pmicobalt_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, 256, + PARAM(DELTA_SOC_THR, DELTA_SOC_THR_WORD, DELTA_SOC_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), @@ -187,7 +187,7 @@ static struct fg_sram_param pmicobalt_v2_sram_params[] = { 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, - 256, 100, 0, fg_encode_default, NULL), + 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), From 9435c04228a8117f7acba15169820e27dc8ed398 Mon Sep 17 00:00:00 2001 From: Nicholas Troast Date: Fri, 23 Sep 2016 08:54:13 -0700 Subject: [PATCH 4/5] qpnp-fg-gen3: disable msoc-full and bsoc-delta IRQs as wakeup sources msoc-full is not used to determine when the battery is full. The charger will wakeup the device when it has terminated charging indicating that the battery is full. Disable the msoc-full IRQ as a wakeup source. The msoc-delta IRQ is used to wakeup the device when the SOC changes therefore having the bsoc-delta IRQ as a wakeup source is unnecessary. Disable the bsoc-delta IRQ as a wakeup source. Change-Id: Iab7087ce3d6727abc852d3a84590cfa570ec584d Signed-off-by: Nicholas Troast --- drivers/power/qcom-charger/fg-core.h | 2 +- drivers/power/qcom-charger/qpnp-fg-gen3.c | 71 ++++++++++++++++++----- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h index 7c6dd45ae412..24ab83565fc9 100644 --- a/drivers/power/qcom-charger/fg-core.h +++ b/drivers/power/qcom-charger/fg-core.h @@ -205,8 +205,8 @@ struct fg_cyc_ctr_data { struct fg_irq_info { const char *name; const irq_handler_t handler; - int irq; bool wakeable; + int irq; }; struct fg_chip { diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c index 5545dd17cdd8..ac24ad9486d9 100644 --- a/drivers/power/qcom-charger/qpnp-fg-gen3.c +++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c @@ -1460,39 +1460,78 @@ static irqreturn_t fg_dummy_irq_handler(int irq, void *data) static struct fg_irq_info fg_irqs[FG_IRQ_MAX] = { /* BATT_SOC irqs */ [MSOC_FULL_IRQ] = { - "msoc-full", fg_soc_irq_handler, true }, + .name = "msoc-full", + .handler = fg_soc_irq_handler, + }, [MSOC_HIGH_IRQ] = { - "msoc-high", fg_soc_irq_handler, true }, + .name = "msoc-high", + .handler = fg_soc_irq_handler, + .wakeable = true, + }, [MSOC_EMPTY_IRQ] = { - "msoc-empty", fg_empty_soc_irq_handler, true }, + .name = "msoc-empty", + .handler = fg_empty_soc_irq_handler, + .wakeable = true, + }, [MSOC_LOW_IRQ] = { - "msoc-low", fg_soc_irq_handler }, + .name = "msoc-low", + .handler = fg_soc_irq_handler, + .wakeable = true, + }, [MSOC_DELTA_IRQ] = { - "msoc-delta", fg_delta_soc_irq_handler, true }, + .name = "msoc-delta", + .handler = fg_delta_soc_irq_handler, + .wakeable = true, + }, [BSOC_DELTA_IRQ] = { - "bsoc-delta", fg_delta_soc_irq_handler, true }, + .name = "bsoc-delta", + .handler = fg_dummy_irq_handler, + }, [SOC_READY_IRQ] = { - "soc-ready", fg_first_est_irq_handler, true }, + .name = "soc-ready", + .handler = fg_first_est_irq_handler, + .wakeable = true, + }, [SOC_UPDATE_IRQ] = { - "soc-update", fg_soc_update_irq_handler }, + .name = "soc-update", + .handler = fg_soc_update_irq_handler, + }, /* BATT_INFO irqs */ [BATT_TEMP_DELTA_IRQ] = { - "batt-temp-delta", fg_delta_batt_temp_irq_handler }, + .name = "batt-temp-delta", + .handler = fg_delta_batt_temp_irq_handler, + }, [BATT_MISSING_IRQ] = { - "batt-missing", fg_batt_missing_irq_handler, true }, + .name = "batt-missing", + .handler = fg_batt_missing_irq_handler, + .wakeable = true, + }, [ESR_DELTA_IRQ] = { - "esr-delta", fg_dummy_irq_handler }, + .name = "esr-delta", + .handler = fg_dummy_irq_handler, + }, [VBATT_LOW_IRQ] = { - "vbatt-low", fg_vbatt_low_irq_handler, true }, + .name = "vbatt-low", + .handler = fg_vbatt_low_irq_handler, + .wakeable = true, + }, [VBATT_PRED_DELTA_IRQ] = { - "vbatt-pred-delta", fg_dummy_irq_handler }, + .name = "vbatt-pred-delta", + .handler = fg_dummy_irq_handler, + }, /* MEM_IF irqs */ [DMA_GRANT_IRQ] = { - "dma-grant", fg_dummy_irq_handler }, + .name = "dma-grant", + .handler = fg_dummy_irq_handler, + }, [MEM_XCP_IRQ] = { - "mem-xcp", fg_dummy_irq_handler }, + .name = "mem-xcp", + .handler = fg_dummy_irq_handler, + }, [IMA_RDY_IRQ] = { - "ima-rdy", fg_dummy_irq_handler }, + .name = "ima-rdy", + .handler = fg_dummy_irq_handler, + }, }; static int fg_get_irq_index_byname(const char *name) From bd811f3afde01a284c8892dba5a7acecdf56975f Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Wed, 21 Sep 2016 12:36:03 -0700 Subject: [PATCH 5/5] qpnp-fg-gen3: Add support to configure force battery profile loading Currently, FG_GEN3 driver loads the battery profile if it finds out the profile loaded earlier doesn't match with the available profile. This will be done when the profile integrity bit is set already. Make this force profile loading as a configurable parameter through a device tree property. While at it, add a module parameter to dump the profile contents conditionally. This will be helpful in debugging. Change-Id: Ibaff55aa5434ee283e6d597485d193e9e52bb74e Signed-off-by: Subbaraman Narayanamurthy --- .../power/qcom-charger/qpnp-fg-gen3.txt | 7 ++ drivers/power/qcom-charger/fg-core.h | 1 + drivers/power/qcom-charger/qpnp-fg-gen3.c | 77 +++++++++++++++---- 3 files changed, 72 insertions(+), 13 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 9fa55eb0ce0d..894924c56947 100644 --- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt @@ -146,6 +146,13 @@ First Level Node - FG Gen3 device Value type: Definition: Enables the cycle counter feature. +- qcom,fg-force-load-profile + Usage: optional + 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. + ========================================================== 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 24ab83565fc9..e4715fd6fb39 100644 --- a/drivers/power/qcom-charger/fg-core.h +++ b/drivers/power/qcom-charger/fg-core.h @@ -181,6 +181,7 @@ struct fg_dt_props { int esr_timer_charging; int esr_timer_awake; int esr_timer_asleep; + bool force_load_profile; }; /* parameters from battery profile */ diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c index ac24ad9486d9..76c26ca7c800 100644 --- a/drivers/power/qcom-charger/qpnp-fg-gen3.c +++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c @@ -274,6 +274,12 @@ module_param_named( sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR ); +static bool fg_sram_dump; +module_param_named( + sram_dump, fg_sram_dump, bool, S_IRUSR | S_IWUSR +); + + /* All getters HERE */ static int fg_decode_value_16b(struct fg_sram_param *sp, @@ -929,40 +935,82 @@ static int fg_get_cycle_count(struct fg_chip *chip) return count; } -#define SOC_READY_WAIT_MS 2000 -static void profile_load_work(struct work_struct *work) +static void dump_sram(u8 *buf, int len) +{ + int i; + char str[16]; + + for (i = 0; i < len; i += 4) { + str[0] = '\0'; + fill_string(str, sizeof(str), buf + i, 4); + pr_info("%03d %s\n", PROFILE_LOAD_WORD + (i / 4), str); + } +} + +static bool is_profile_load_required(struct fg_chip *chip) { - struct fg_chip *chip = container_of(work, - struct fg_chip, - profile_load_work.work); - int rc; u8 buf[PROFILE_COMP_LEN], val; - bool tried_again = false, profiles_same = false; + bool profiles_same = false; + int rc; rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD, PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); if (rc < 0) { pr_err("failed to read profile integrity rc=%d\n", rc); - return; + return false; } - vote(chip->awake_votable, PROFILE_LOAD, true, 0); + /* Check if integrity bit is set */ if (val == 0x01) { fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n"); rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT); if (rc < 0) { pr_err("Error in reading battery profile, rc:%d\n", rc); - goto out; + return false; } profiles_same = memcmp(chip->batt_profile, buf, PROFILE_COMP_LEN) == 0; if (profiles_same) { - fg_dbg(chip, FG_STATUS, "Battery profile is same\n"); - goto done; + fg_dbg(chip, FG_STATUS, "Battery profile is same, not loading it\n"); + return false; + } + + if (!chip->dt.force_load_profile) { + pr_warn("Profiles doesn't match, skipping loading it since force_load_profile is disabled\n"); + if (fg_sram_dump) { + pr_info("FG: loaded profile:\n"); + dump_sram(buf, PROFILE_COMP_LEN); + pr_info("FG: available profile:\n"); + dump_sram(chip->batt_profile, PROFILE_LEN); + } + return false; + } + + fg_dbg(chip, FG_STATUS, "Profiles are different, loading the correct one\n"); + } else { + fg_dbg(chip, FG_STATUS, "Profile integrity bit is not set\n"); + if (fg_sram_dump) { + pr_info("FG: profile to be loaded:\n"); + dump_sram(chip->batt_profile, PROFILE_LEN); } - fg_dbg(chip, FG_STATUS, "profiles are different?\n"); } + return true; +} + +#define SOC_READY_WAIT_MS 2000 +static void profile_load_work(struct work_struct *work) +{ + struct fg_chip *chip = container_of(work, + struct fg_chip, + profile_load_work.work); + bool tried_again = false; + u8 buf[2], val; + int rc; + + vote(chip->awake_votable, PROFILE_LOAD, true, 0); + if (!is_profile_load_required(chip)) + goto done; clear_cycle_counter(chip); fg_dbg(chip, FG_STATUS, "profile loading started\n"); @@ -1783,6 +1831,9 @@ static int fg_parse_dt(struct fg_chip *chip) if (chip->cyc_ctr.en) chip->cyc_ctr.id = 1; + chip->dt.force_load_profile = of_property_read_bool(node, + "qcom,fg-force-load-profile"); + return 0; }