summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-10-10 18:28:21 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-10-10 18:28:20 -0700
commitd610af945362876ee99e73c41585c72a7a321c0b (patch)
tree9e3a5f024fbf8270e1fa193d48225062e798d6df /drivers
parent6ce9b4fee46eb3cc0be4aa8392c553a9c6da456b (diff)
parentbd811f3afde01a284c8892dba5a7acecdf56975f (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.h13
-rw-r--r--drivers/power/qcom-charger/qpnp-fg-gen3.c264
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");