diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2016-10-10 18:28:21 -0700 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-10-10 18:28:20 -0700 |
commit | d610af945362876ee99e73c41585c72a7a321c0b (patch) | |
tree | 9e3a5f024fbf8270e1fa193d48225062e798d6df /drivers | |
parent | 6ce9b4fee46eb3cc0be4aa8392c553a9c6da456b (diff) | |
parent | bd811f3afde01a284c8892dba5a7acecdf56975f (diff) |
Merge "qpnp-fg-gen3: Add support to configure force battery profile loading"
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/power/qcom-charger/fg-core.h | 13 | ||||
-rw-r--r-- | drivers/power/qcom-charger/qpnp-fg-gen3.c | 264 |
2 files changed, 196 insertions, 81 deletions
diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h index 515f31a44ce7..e4715fd6fb39 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, @@ -177,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 */ @@ -184,6 +189,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; }; @@ -200,8 +206,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 { @@ -217,7 +223,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 +233,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..76c26ca7c800 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,11 +138,13 @@ 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, 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), @@ -172,12 +178,16 @@ 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, 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), @@ -264,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, @@ -302,14 +318,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 +335,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 +609,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 +628,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 +667,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 +685,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,46 +935,82 @@ 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) +static void dump_sram(u8 *buf, int len) { - 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; + int i; + char str[16]; - if (!chip->batt_id_avail) { - pr_err("batt_id not available\n"); - return; + 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) +{ + u8 buf[PROFILE_COMP_LEN], val; + 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"); @@ -1182,6 +1241,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 +1396,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 +1423,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; @@ -1445,39 +1508,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) @@ -1638,6 +1740,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) @@ -1724,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; } @@ -1836,10 +1946,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"); |