qpnp-fg-gen3: add support for dumping FG SRAM

Add support to dump FG SRAM periodically based on the module
parameters. This will be useful for debugging purpose.

To enable FG SRAM dump,
echo 1 > /sys/module/qpnp_fg_gen3/parameters/sram_dump_en

To disable FG SRAM dump,
echo 0 > /sys/module/qpnp_fg_gen3/parameters/sram_dump_en

To set FG SRAM dump period,
echo 15000 > /sys/module/qpnp_fg_gen3/parameters/sram_dump_period_ms

Change-Id: Ib4bae7f67100a4bda1e4b996f2fbaeb86da979d2
Signed-off-by: Subbaraman Narayanamurthy <subbaram@codeaurora.org>
This commit is contained in:
Subbaraman Narayanamurthy 2016-12-09 16:43:52 -08:00
parent 2f817b68f0
commit 1897bd682b
3 changed files with 109 additions and 26 deletions

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -62,6 +62,7 @@
CHARS_PER_ITEM) + 1) \
#define FG_SRAM_ADDRESS_MAX 255
#define FG_SRAM_LEN 504
#define PROFILE_LEN 224
#define PROFILE_COMP_LEN 148
#define BUCKET_COUNT 8
@ -342,6 +343,7 @@ struct fg_chip {
struct work_struct status_change_work;
struct work_struct cycle_count_work;
struct delayed_work batt_avg_work;
struct delayed_work sram_dump_work;
struct fg_circ_buf ibatt_circ_buf;
struct fg_circ_buf vbatt_circ_buf;
};
@ -392,6 +394,7 @@ extern int fg_clear_ima_errors_if_any(struct fg_chip *chip, bool check_hw_sts);
extern int fg_clear_dma_errors_if_any(struct fg_chip *chip);
extern int fg_debugfs_create(struct fg_chip *chip);
extern void fill_string(char *str, size_t str_len, u8 *buf, int buf_len);
extern void dump_sram(u8 *buf, int addr, int len);
extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos);
extern s64 fg_float_decode(u16 val);
extern bool is_input_present(struct fg_chip *chip);

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -10,8 +10,6 @@
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "FG: %s: " fmt, __func__
#include "fg-core.h"
void fg_circ_buf_add(struct fg_circ_buf *buf, int val)
@ -174,6 +172,24 @@ void fill_string(char *str, size_t str_len, u8 *buf, int buf_len)
}
}
void dump_sram(u8 *buf, int addr, int len)
{
int i;
char str[16];
/*
* Length passed should be in multiple of 4 as each FG SRAM word
* holds 4 bytes. To keep this simple, even if a length which is
* not a multiple of 4 bytes or less than 4 bytes is passed, SRAM
* registers dumped will be always in multiple of 4 bytes.
*/
for (i = 0; i < len; i += 4) {
str[0] = '\0';
fill_string(str, sizeof(str), buf + i, 4);
pr_info("%03d %s\n", addr + (i / 4), str);
}
}
static inline bool fg_sram_address_valid(u16 address, int len)
{
if (address > FG_SRAM_ADDRESS_MAX)

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -12,6 +12,7 @@
#define pr_fmt(fmt) "FG: %s: " fmt, __func__
#include <linux/ktime.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
@ -330,17 +331,18 @@ module_param_named(
debug_mask, fg_gen3_debug_mask, int, S_IRUSR | S_IWUSR
);
static int fg_sram_update_period_ms = 30000;
static bool fg_profile_dump;
module_param_named(
sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR
profile_dump, fg_profile_dump, bool, S_IRUSR | S_IWUSR
);
static bool fg_sram_dump;
static int fg_sram_dump_period_ms = 20000;
module_param_named(
sram_dump, fg_sram_dump, bool, S_IRUSR | S_IWUSR
sram_dump_period_ms, fg_sram_dump_period_ms, int, S_IRUSR | S_IWUSR
);
static int fg_restart;
static bool fg_sram_dump;
/* All getters HERE */
@ -1838,18 +1840,6 @@ static int fg_get_cycle_count(struct fg_chip *chip)
return count;
}
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);
}
}
#define PROFILE_LOAD_BIT BIT(0)
#define BOOTLOADER_LOAD_BIT BIT(1)
#define BOOTLOADER_RESTART_BIT BIT(2)
@ -1885,11 +1875,13 @@ static bool is_profile_load_required(struct fg_chip *chip)
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) {
if (fg_profile_dump) {
pr_info("FG: loaded profile:\n");
dump_sram(buf, PROFILE_COMP_LEN);
dump_sram(buf, PROFILE_LOAD_WORD,
PROFILE_COMP_LEN);
pr_info("FG: available profile:\n");
dump_sram(chip->batt_profile, PROFILE_LEN);
dump_sram(chip->batt_profile, PROFILE_LOAD_WORD,
PROFILE_LEN);
}
return false;
}
@ -1897,9 +1889,10 @@ static bool is_profile_load_required(struct fg_chip *chip)
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) {
if (fg_profile_dump) {
pr_info("FG: profile to be loaded:\n");
dump_sram(chip->batt_profile, PROFILE_LEN);
dump_sram(chip->batt_profile, PROFILE_LOAD_WORD,
PROFILE_LEN);
}
}
return true;
@ -2056,6 +2049,71 @@ out:
vote(chip->awake_votable, PROFILE_LOAD, false, 0);
}
static void sram_dump_work(struct work_struct *work)
{
struct fg_chip *chip = container_of(work, struct fg_chip,
sram_dump_work.work);
u8 buf[FG_SRAM_LEN];
int rc;
s64 timestamp_ms;
rc = fg_sram_read(chip, 0, 0, buf, FG_SRAM_LEN, FG_IMA_DEFAULT);
if (rc < 0) {
pr_err("Error in reading FG SRAM, rc:%d\n", rc);
goto resched;
}
timestamp_ms = ktime_to_ms(ktime_get_boottime());
fg_dbg(chip, FG_STATUS, "SRAM Dump Started at %lld.%lld\n",
timestamp_ms / 1000, timestamp_ms % 1000);
dump_sram(buf, 0, FG_SRAM_LEN);
timestamp_ms = ktime_to_ms(ktime_get_boottime());
fg_dbg(chip, FG_STATUS, "SRAM Dump done at %lld.%lld\n",
timestamp_ms / 1000, timestamp_ms % 1000);
resched:
schedule_delayed_work(&chip->sram_dump_work,
msecs_to_jiffies(fg_sram_dump_period_ms));
}
static int fg_sram_dump_sysfs(const char *val, const struct kernel_param *kp)
{
int rc;
struct power_supply *bms_psy;
struct fg_chip *chip;
bool old_val = fg_sram_dump;
rc = param_set_bool(val, kp);
if (rc) {
pr_err("Unable to set fg_sram_dump: %d\n", rc);
return rc;
}
if (fg_sram_dump == old_val)
return 0;
bms_psy = power_supply_get_by_name("bms");
if (!bms_psy) {
pr_err("bms psy not found\n");
return -ENODEV;
}
chip = power_supply_get_drvdata(bms_psy);
if (fg_sram_dump)
schedule_delayed_work(&chip->sram_dump_work,
msecs_to_jiffies(fg_sram_dump_period_ms));
else
cancel_delayed_work_sync(&chip->sram_dump_work);
return 0;
}
static struct kernel_param_ops fg_sram_dump_ops = {
.set = fg_sram_dump_sysfs,
.get = param_get_bool,
};
module_param_cb(sram_dump_en, &fg_sram_dump_ops, &fg_sram_dump, 0644);
static int fg_restart_sysfs(const char *val, const struct kernel_param *kp)
{
int rc;
@ -3449,6 +3507,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
INIT_WORK(&chip->status_change_work, status_change_work);
INIT_WORK(&chip->cycle_count_work, cycle_count_work);
INIT_DELAYED_WORK(&chip->batt_avg_work, batt_avg_work);
INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work);
rc = fg_memif_init(chip);
if (rc < 0) {
@ -3542,6 +3601,8 @@ static int fg_gen3_suspend(struct device *dev)
}
cancel_delayed_work_sync(&chip->batt_avg_work);
if (fg_sram_dump)
cancel_delayed_work_sync(&chip->sram_dump_work);
return 0;
}
@ -3563,6 +3624,9 @@ static int fg_gen3_resume(struct device *dev)
fg_circ_buf_clr(&chip->ibatt_circ_buf);
fg_circ_buf_clr(&chip->vbatt_circ_buf);
schedule_delayed_work(&chip->batt_avg_work, 0);
if (fg_sram_dump)
schedule_delayed_work(&chip->sram_dump_work,
msecs_to_jiffies(fg_sram_dump_period_ms));
return 0;
}