Loading arch/arm64/configs/vendor/lahaina_debug.config +1 −0 Original line number Diff line number Diff line Loading @@ -63,3 +63,4 @@ CONFIG_QRTR_MHI=y CONFIG_DEBUG_PANIC_ON_OOM=y CONFIG_USB_F_FS_IPC_LOGGING=y CONFIG_DYNAMIC_DEBUG=y CONFIG_QTI_PMIC_GLINK_CLIENT_DEBUG=y drivers/soc/qcom/Kconfig +10 −0 Original line number Diff line number Diff line Loading @@ -575,6 +575,16 @@ config QTI_PMIC_GLINK charging and gauging. This enables clients to read and write battery charging parameters. config QTI_PMIC_GLINK_CLIENT_DEBUG depends on QTI_PMIC_GLINK && DEBUG_FS bool "Enable debugfs features in PMIC GLINK client drivers" help This option enables the generation of debugfs files in PMIC GLINK client drivers that are strictly meant for internal debugging only. Writing to these debug files changes key physical parameters of a system, which may lead to instability. Therefore, this option should never be enabled on production devices. config QTI_BATTERY_GLINK_DEBUG tristate "Enable support for QTI battery glink debug driver" depends on QTI_PMIC_GLINK Loading drivers/soc/qcom/qti_battery_debug.c +523 −12 Original line number Diff line number Diff line Loading @@ -14,15 +14,47 @@ #include <linux/slab.h> #include <linux/soc/qcom/pmic_glink.h> /* owner/type/opcode for battery debug */ /* owner/type/opcodes for battery debug */ #define MSG_OWNER_BD 32781 #define MSG_TYPE_REQ_RESP 1 #define BD_GET_AGGREGATOR_INFO_REQ 0x15 #define BD_OVERWRITE_VOTABLE_REQ 0x16 #define BD_GET_VOTABLE_REQ 0x17 #define BD_QBG_DUMP_REQ 0x36 /* Generic definitions */ #define MAX_BUF_LEN SZ_4K #define BD_WAIT_TIME_MS 1000 #define MAX_NUM_VOTABLES 12 #define MAX_NUM_VOTERS 32 #define MAX_NAME_LEN 12 struct all_votables_data { u32 num_votables; u32 num_voters; char votables[MAX_NUM_VOTABLES][MAX_NAME_LEN]; char voters[MAX_NUM_VOTERS][MAX_NAME_LEN]; }; struct votable_data { u32 votable_id; u32 eff_val; u8 voter_ids[MAX_NUM_VOTERS]; /* unused */ u32 votes[MAX_NUM_VOTERS]; u32 active_voter_mask; u32 eff_voter; u32 override_voter; }; struct votable { char *name; u32 id; struct battery_dbg_dev *bd; struct votable_data data; u32 override_val; }; struct qbg_context_req_msg { struct pmic_glink_hdr hdr; }; Loading @@ -33,6 +65,31 @@ struct qbg_context_resp_msg { u8 buf[MAX_BUF_LEN]; }; struct votables_list_req_msg { struct pmic_glink_hdr hdr; }; struct votables_list_resp_msg { struct pmic_glink_hdr hdr; struct all_votables_data all_data; }; struct votable_req_msg { struct pmic_glink_hdr hdr; u32 votable_id; }; struct votable_resp_msg { struct pmic_glink_hdr hdr; struct votable_data v_data; }; struct override_req_msg { struct pmic_glink_hdr hdr; u32 votable_id; u32 override_val; }; struct battery_dbg_dev { struct device *dev; struct pmic_glink_client *client; Loading @@ -41,6 +98,9 @@ struct battery_dbg_dev { struct qbg_context_resp_msg qbg_dump; struct dentry *debugfs_dir; struct debugfs_blob_wrapper qbg_blob; struct all_votables_data all_data; struct votable *votable; u8 override_voter_id; }; static int battery_dbg_write(struct battery_dbg_dev *bd, void *data, size_t len) Loading Loading @@ -91,6 +151,89 @@ static void handle_qbg_dump_message(struct battery_dbg_dev *bd, complete(&bd->ack); } static void handle_override_message(struct battery_dbg_dev *bd, void *unused, size_t len) { pr_debug("override succeeded\n"); complete(&bd->ack); } static void handle_get_votable_message(struct battery_dbg_dev *bd, struct votable_resp_msg *resp_msg, size_t len) { u32 id = resp_msg->v_data.votable_id; if (len != sizeof(*resp_msg)) { pr_err("Expected data length: %zu, received: %zu\n", sizeof(*resp_msg), len); return; } if (id >= MAX_NUM_VOTABLES) { pr_err("Votable id %u exceeds max %d\n", id, MAX_NUM_VOTABLES); return; } if (resp_msg->v_data.active_voter_mask && resp_msg->v_data.eff_voter >= MAX_NUM_VOTERS) { pr_err("Effective voter id %u exceeds max %d\n", resp_msg->v_data.eff_voter, MAX_NUM_VOTERS); return; } memcpy(&bd->votable[id].data, &resp_msg->v_data, sizeof(resp_msg->v_data)); complete(&bd->ack); } #define OVERRIDE_VOTER_NAME "glink" static void handle_get_votables_list_message(struct battery_dbg_dev *bd, struct votables_list_resp_msg *resp_msg, size_t len) { u8 i; if (len != sizeof(*resp_msg)) { pr_err("Expected data length: %zu, received: %zu\n", sizeof(*resp_msg), len); return; } if (resp_msg->all_data.num_votables >= MAX_NUM_VOTABLES) { pr_err("Num votables %u exceeds max %d\n", resp_msg->all_data.num_votables, MAX_NUM_VOTABLES); return; } if (resp_msg->all_data.num_voters >= MAX_NUM_VOTERS) { pr_err("Num voters %u exceeds max %d\n", resp_msg->all_data.num_voters, MAX_NUM_VOTERS); return; } memcpy(&bd->all_data, &resp_msg->all_data, sizeof(resp_msg->all_data)); for (i = 0; i < MAX_NUM_VOTABLES; i++) bd->all_data.votables[i][MAX_NAME_LEN - 1] = '\0'; for (i = 0; i < MAX_NUM_VOTERS; i++) bd->all_data.voters[i][MAX_NAME_LEN - 1] = '\0'; if (!bd->override_voter_id) { for (i = 0; i < MAX_NUM_VOTERS; i++) { if (!strcmp(bd->all_data.voters[i], OVERRIDE_VOTER_NAME)) { bd->override_voter_id = i; break; } } } complete(&bd->ack); } static int battery_dbg_callback(void *priv, void *data, size_t len) { struct pmic_glink_hdr *hdr = data; Loading @@ -103,6 +246,15 @@ static int battery_dbg_callback(void *priv, void *data, size_t len) case BD_QBG_DUMP_REQ: handle_qbg_dump_message(bd, data, len); break; case BD_GET_AGGREGATOR_INFO_REQ: handle_get_votables_list_message(bd, data, len); break; case BD_GET_VOTABLE_REQ: handle_get_votable_message(bd, data, len); break; case BD_OVERWRITE_VOTABLE_REQ: handle_override_message(bd, data, len); break; default: pr_err("Unknown opcode %u\n", hdr->opcode); break; Loading @@ -126,28 +278,387 @@ static int get_qbg_context_write(void *data, u64 val) DEFINE_DEBUGFS_ATTRIBUTE(get_qbg_context_debugfs_ops, NULL, get_qbg_context_write, "%llu\n"); static int battery_dbg_request_read_votable(struct battery_dbg_dev *bd, u32 id) { struct votable_req_msg req_msg = { { 0 } }; req_msg.hdr.owner = MSG_OWNER_BD; req_msg.hdr.type = MSG_TYPE_REQ_RESP; req_msg.hdr.opcode = BD_GET_VOTABLE_REQ; req_msg.votable_id = id; return battery_dbg_write(bd, &req_msg, sizeof(req_msg)); } #ifdef CONFIG_QTI_PMIC_GLINK_CLIENT_DEBUG static int battery_dbg_request_override(struct battery_dbg_dev *bd, u32 id, u32 val) { struct override_req_msg req_msg = { { 0 } }; req_msg.hdr.owner = MSG_OWNER_BD; req_msg.hdr.type = MSG_TYPE_REQ_RESP; req_msg.hdr.opcode = BD_OVERWRITE_VOTABLE_REQ; req_msg.votable_id = id; req_msg.override_val = val; pr_debug("requesting override of %s with value %u\n", bd->votable[id].name, val); return battery_dbg_write(bd, &req_msg, sizeof(req_msg)); } #endif static int active_show(struct seq_file *s, void *unused) { int rc; unsigned long voter_mask; struct votable *v = s->private; struct battery_dbg_dev *bd = v->bd; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; seq_printf(s, "%#x\n", voter_mask); return 0; } DEFINE_SHOW_ATTRIBUTE(active); static int winvote_show(struct seq_file *s, void *unused) { int rc, winvote; unsigned long voter_mask; struct votable *v = s->private; struct battery_dbg_dev *bd = v->bd; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; winvote = voter_mask ? v->data.eff_val : -EINVAL; seq_printf(s, "%d\n", winvote); return 0; } DEFINE_SHOW_ATTRIBUTE(winvote); static int winner_show(struct seq_file *s, void *unused) { int rc; char *winner; u32 eff_voter; unsigned long voter_mask; struct votable *v = s->private; struct battery_dbg_dev *bd = v->bd; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; if (voter_mask) { eff_voter = v->data.eff_voter; winner = bd->all_data.voters[eff_voter]; } else { winner = ""; } seq_printf(s, "%s\n", winner); return 0; } DEFINE_SHOW_ATTRIBUTE(winner); static int voters_show(struct seq_file *s, void *unused) { int rc, i; unsigned long voter_mask; struct votable *v = s->private; struct battery_dbg_dev *bd = v->bd; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; for_each_set_bit(i, &voter_mask, MAX_NUM_VOTERS) seq_printf(s, "%s ", bd->all_data.voters[i]); seq_puts(s, "\n"); return 0; } DEFINE_SHOW_ATTRIBUTE(voters); static int votes_show(struct seq_file *s, void *unused) { int rc, i; unsigned long voter_mask; struct votable *v = s->private; struct battery_dbg_dev *bd = v->bd; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; for_each_set_bit(i, &voter_mask, MAX_NUM_VOTERS) seq_printf(s, "%d ", v->data.votes[i]); seq_puts(s, "\n"); return 0; } DEFINE_SHOW_ATTRIBUTE(votes); static int status_show(struct seq_file *s, void *unused) { int rc, i, winvote; char *winner; u32 eff_voter; unsigned long voter_mask; struct votable *v = s->private; struct battery_dbg_dev *bd = v->bd; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; if (!voter_mask) { seq_puts(s, "\n"); return 0; } winvote = v->data.eff_val; eff_voter = v->data.eff_voter; winner = bd->all_data.voters[eff_voter]; for_each_set_bit(i, &voter_mask, MAX_NUM_VOTERS) seq_printf(s, " %-*s: %-*s: %d\n", MAX_NAME_LEN, v->name, MAX_NAME_LEN, bd->all_data.voters[i], v->data.votes[i]); seq_printf(s, "EFFECTIVE: %-*s: %-*s: %d\n", MAX_NAME_LEN, v->name, MAX_NAME_LEN, winner, winvote); return 0; } DEFINE_SHOW_ATTRIBUTE(status); #ifdef CONFIG_QTI_PMIC_GLINK_CLIENT_DEBUG static int override_get(void *data, u64 *val) { int rc; struct votable *v = data; struct battery_dbg_dev *bd = v->bd; unsigned long voter_mask; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; /* Show override voter's vote only if override voter is active */ if (test_bit(bd->override_voter_id, &voter_mask)) *val = v->data.votes[bd->override_voter_id]; else *val = 0; return 0; } static int override_set(void *data, u64 val) { int rc; struct votable *v = data; struct battery_dbg_dev *bd = v->bd; u32 set = val; rc = battery_dbg_request_override(bd, v->id, set); if (rc) { pr_err("%s override request failed: %d\n", v->name, rc); return rc; } return 0; } DEFINE_DEBUGFS_ATTRIBUTE(override_fops, override_get, override_set, "%llu\n"); static int battery_dbg_create_override_file(struct votable *v, struct dentry *votable_dir) { return PTR_ERR_OR_ZERO(debugfs_create_file_unsafe("override", 0600, votable_dir, v, &override_fops)); } #else static int battery_dbg_create_override_file(struct votable *v, struct dentry *votable_dir) { return 0; } #endif static int battery_dbg_create_votable(struct battery_dbg_dev *bd, struct dentry *votables_root_dir, u32 id) { int rc; char *v_name = bd->all_data.votables[id]; struct dentry *votable_dir; votable_dir = debugfs_create_dir(v_name, votables_root_dir); if (IS_ERR(votable_dir)) { pr_err("Failed to create %s debugfs directory\n", v_name); return PTR_ERR(votable_dir); } bd->votable[id].name = v_name; rc = PTR_ERR_OR_ZERO(debugfs_create_file("active", 0400, votable_dir, &bd->votable[id], &active_fops)); if (rc) goto error; rc = PTR_ERR_OR_ZERO(debugfs_create_file("winvote", 0400, votable_dir, &bd->votable[id], &winvote_fops)); if (rc) goto error; rc = PTR_ERR_OR_ZERO(debugfs_create_file("winner", 0400, votable_dir, &bd->votable[id], &winner_fops)); if (rc) goto error; rc = PTR_ERR_OR_ZERO(debugfs_create_file("voters", 0400, votable_dir, &bd->votable[id], &voters_fops)); if (rc) goto error; rc = PTR_ERR_OR_ZERO(debugfs_create_file("votes", 0400, votable_dir, &bd->votable[id], &votes_fops)); if (rc) goto error; rc = PTR_ERR_OR_ZERO(debugfs_create_file("status", 0400, votable_dir, &bd->votable[id], &status_fops)); if (rc) goto error; rc = battery_dbg_create_override_file(&bd->votable[id], votable_dir); if (rc) goto error; return 0; error: pr_err("Failed to create debugfs file: %d\n", rc); return rc; } static int battery_dbg_get_votables_list(struct battery_dbg_dev *bd) { struct votable_req_msg req_msg = { { 0 } }; req_msg.hdr.owner = MSG_OWNER_BD; req_msg.hdr.type = MSG_TYPE_REQ_RESP; req_msg.hdr.opcode = BD_GET_AGGREGATOR_INFO_REQ; return battery_dbg_write(bd, &req_msg, sizeof(req_msg)); } static int battery_dbg_create_votables(struct battery_dbg_dev *bd, struct dentry *bd_root_dir) { int rc, id; u32 num_votables; struct dentry *votables_root_dir; votables_root_dir = debugfs_create_dir("votables", bd_root_dir); if (IS_ERR(votables_root_dir)) { pr_err("Failed to create votables root directory\n"); return PTR_ERR(votables_root_dir); } rc = battery_dbg_get_votables_list(bd); if (rc) { pr_err("Failed to get votables list: %d\n", rc); return rc; } num_votables = bd->all_data.num_votables; bd->votable = devm_kcalloc(bd->dev, num_votables, sizeof(struct votable), GFP_KERNEL); for (id = 0; id < num_votables; id++) { bd->votable[id].bd = bd; bd->votable[id].id = id; rc = battery_dbg_create_votable(bd, votables_root_dir, id); if (rc) return rc; } return 0; } static int battery_dbg_add_debugfs(struct battery_dbg_dev *bd) { int rc; struct dentry *bd_dir, *file; bd_dir = debugfs_create_dir("battery_debug", NULL); if (!bd_dir) { pr_err("Failed to create battery debugfs directory\n"); return -ENOMEM; if (IS_ERR(bd_dir)) { rc = PTR_ERR(bd_dir); pr_err("Failed to create battery debugfs directory: %d\n", rc); return rc; } file = debugfs_create_file_unsafe("get_qbg_context", 0200, bd_dir, bd, &get_qbg_context_debugfs_ops); if (!file) { pr_err("Failed to create get_qbg_context debugfs file\n"); if (IS_ERR(file)) { rc = PTR_ERR(file); pr_err("Failed to create get_qbg_context debugfs file: %d\n", rc); goto error; } bd->qbg_blob.data = bd->qbg_dump.buf; bd->qbg_blob.size = 0; file = debugfs_create_blob("qbg_context", 0444, bd_dir, &bd->qbg_blob); if (!file) { pr_err("Failed to create qbg_context debugfs file\n"); if (IS_ERR(file)) { rc = PTR_ERR(file); pr_err("Failed to create qbg_context debugfs file: %d\n", rc); goto error; } rc = battery_dbg_create_votables(bd, bd_dir); if (rc) { pr_err("Failed to create votables: %d\n", rc); goto error; } Loading @@ -156,7 +667,7 @@ static int battery_dbg_add_debugfs(struct battery_dbg_dev *bd) return 0; error: debugfs_remove_recursive(bd_dir); return -ENOMEM; return rc; } static int battery_dbg_probe(struct platform_device *pdev) Loading Loading
arch/arm64/configs/vendor/lahaina_debug.config +1 −0 Original line number Diff line number Diff line Loading @@ -63,3 +63,4 @@ CONFIG_QRTR_MHI=y CONFIG_DEBUG_PANIC_ON_OOM=y CONFIG_USB_F_FS_IPC_LOGGING=y CONFIG_DYNAMIC_DEBUG=y CONFIG_QTI_PMIC_GLINK_CLIENT_DEBUG=y
drivers/soc/qcom/Kconfig +10 −0 Original line number Diff line number Diff line Loading @@ -575,6 +575,16 @@ config QTI_PMIC_GLINK charging and gauging. This enables clients to read and write battery charging parameters. config QTI_PMIC_GLINK_CLIENT_DEBUG depends on QTI_PMIC_GLINK && DEBUG_FS bool "Enable debugfs features in PMIC GLINK client drivers" help This option enables the generation of debugfs files in PMIC GLINK client drivers that are strictly meant for internal debugging only. Writing to these debug files changes key physical parameters of a system, which may lead to instability. Therefore, this option should never be enabled on production devices. config QTI_BATTERY_GLINK_DEBUG tristate "Enable support for QTI battery glink debug driver" depends on QTI_PMIC_GLINK Loading
drivers/soc/qcom/qti_battery_debug.c +523 −12 Original line number Diff line number Diff line Loading @@ -14,15 +14,47 @@ #include <linux/slab.h> #include <linux/soc/qcom/pmic_glink.h> /* owner/type/opcode for battery debug */ /* owner/type/opcodes for battery debug */ #define MSG_OWNER_BD 32781 #define MSG_TYPE_REQ_RESP 1 #define BD_GET_AGGREGATOR_INFO_REQ 0x15 #define BD_OVERWRITE_VOTABLE_REQ 0x16 #define BD_GET_VOTABLE_REQ 0x17 #define BD_QBG_DUMP_REQ 0x36 /* Generic definitions */ #define MAX_BUF_LEN SZ_4K #define BD_WAIT_TIME_MS 1000 #define MAX_NUM_VOTABLES 12 #define MAX_NUM_VOTERS 32 #define MAX_NAME_LEN 12 struct all_votables_data { u32 num_votables; u32 num_voters; char votables[MAX_NUM_VOTABLES][MAX_NAME_LEN]; char voters[MAX_NUM_VOTERS][MAX_NAME_LEN]; }; struct votable_data { u32 votable_id; u32 eff_val; u8 voter_ids[MAX_NUM_VOTERS]; /* unused */ u32 votes[MAX_NUM_VOTERS]; u32 active_voter_mask; u32 eff_voter; u32 override_voter; }; struct votable { char *name; u32 id; struct battery_dbg_dev *bd; struct votable_data data; u32 override_val; }; struct qbg_context_req_msg { struct pmic_glink_hdr hdr; }; Loading @@ -33,6 +65,31 @@ struct qbg_context_resp_msg { u8 buf[MAX_BUF_LEN]; }; struct votables_list_req_msg { struct pmic_glink_hdr hdr; }; struct votables_list_resp_msg { struct pmic_glink_hdr hdr; struct all_votables_data all_data; }; struct votable_req_msg { struct pmic_glink_hdr hdr; u32 votable_id; }; struct votable_resp_msg { struct pmic_glink_hdr hdr; struct votable_data v_data; }; struct override_req_msg { struct pmic_glink_hdr hdr; u32 votable_id; u32 override_val; }; struct battery_dbg_dev { struct device *dev; struct pmic_glink_client *client; Loading @@ -41,6 +98,9 @@ struct battery_dbg_dev { struct qbg_context_resp_msg qbg_dump; struct dentry *debugfs_dir; struct debugfs_blob_wrapper qbg_blob; struct all_votables_data all_data; struct votable *votable; u8 override_voter_id; }; static int battery_dbg_write(struct battery_dbg_dev *bd, void *data, size_t len) Loading Loading @@ -91,6 +151,89 @@ static void handle_qbg_dump_message(struct battery_dbg_dev *bd, complete(&bd->ack); } static void handle_override_message(struct battery_dbg_dev *bd, void *unused, size_t len) { pr_debug("override succeeded\n"); complete(&bd->ack); } static void handle_get_votable_message(struct battery_dbg_dev *bd, struct votable_resp_msg *resp_msg, size_t len) { u32 id = resp_msg->v_data.votable_id; if (len != sizeof(*resp_msg)) { pr_err("Expected data length: %zu, received: %zu\n", sizeof(*resp_msg), len); return; } if (id >= MAX_NUM_VOTABLES) { pr_err("Votable id %u exceeds max %d\n", id, MAX_NUM_VOTABLES); return; } if (resp_msg->v_data.active_voter_mask && resp_msg->v_data.eff_voter >= MAX_NUM_VOTERS) { pr_err("Effective voter id %u exceeds max %d\n", resp_msg->v_data.eff_voter, MAX_NUM_VOTERS); return; } memcpy(&bd->votable[id].data, &resp_msg->v_data, sizeof(resp_msg->v_data)); complete(&bd->ack); } #define OVERRIDE_VOTER_NAME "glink" static void handle_get_votables_list_message(struct battery_dbg_dev *bd, struct votables_list_resp_msg *resp_msg, size_t len) { u8 i; if (len != sizeof(*resp_msg)) { pr_err("Expected data length: %zu, received: %zu\n", sizeof(*resp_msg), len); return; } if (resp_msg->all_data.num_votables >= MAX_NUM_VOTABLES) { pr_err("Num votables %u exceeds max %d\n", resp_msg->all_data.num_votables, MAX_NUM_VOTABLES); return; } if (resp_msg->all_data.num_voters >= MAX_NUM_VOTERS) { pr_err("Num voters %u exceeds max %d\n", resp_msg->all_data.num_voters, MAX_NUM_VOTERS); return; } memcpy(&bd->all_data, &resp_msg->all_data, sizeof(resp_msg->all_data)); for (i = 0; i < MAX_NUM_VOTABLES; i++) bd->all_data.votables[i][MAX_NAME_LEN - 1] = '\0'; for (i = 0; i < MAX_NUM_VOTERS; i++) bd->all_data.voters[i][MAX_NAME_LEN - 1] = '\0'; if (!bd->override_voter_id) { for (i = 0; i < MAX_NUM_VOTERS; i++) { if (!strcmp(bd->all_data.voters[i], OVERRIDE_VOTER_NAME)) { bd->override_voter_id = i; break; } } } complete(&bd->ack); } static int battery_dbg_callback(void *priv, void *data, size_t len) { struct pmic_glink_hdr *hdr = data; Loading @@ -103,6 +246,15 @@ static int battery_dbg_callback(void *priv, void *data, size_t len) case BD_QBG_DUMP_REQ: handle_qbg_dump_message(bd, data, len); break; case BD_GET_AGGREGATOR_INFO_REQ: handle_get_votables_list_message(bd, data, len); break; case BD_GET_VOTABLE_REQ: handle_get_votable_message(bd, data, len); break; case BD_OVERWRITE_VOTABLE_REQ: handle_override_message(bd, data, len); break; default: pr_err("Unknown opcode %u\n", hdr->opcode); break; Loading @@ -126,28 +278,387 @@ static int get_qbg_context_write(void *data, u64 val) DEFINE_DEBUGFS_ATTRIBUTE(get_qbg_context_debugfs_ops, NULL, get_qbg_context_write, "%llu\n"); static int battery_dbg_request_read_votable(struct battery_dbg_dev *bd, u32 id) { struct votable_req_msg req_msg = { { 0 } }; req_msg.hdr.owner = MSG_OWNER_BD; req_msg.hdr.type = MSG_TYPE_REQ_RESP; req_msg.hdr.opcode = BD_GET_VOTABLE_REQ; req_msg.votable_id = id; return battery_dbg_write(bd, &req_msg, sizeof(req_msg)); } #ifdef CONFIG_QTI_PMIC_GLINK_CLIENT_DEBUG static int battery_dbg_request_override(struct battery_dbg_dev *bd, u32 id, u32 val) { struct override_req_msg req_msg = { { 0 } }; req_msg.hdr.owner = MSG_OWNER_BD; req_msg.hdr.type = MSG_TYPE_REQ_RESP; req_msg.hdr.opcode = BD_OVERWRITE_VOTABLE_REQ; req_msg.votable_id = id; req_msg.override_val = val; pr_debug("requesting override of %s with value %u\n", bd->votable[id].name, val); return battery_dbg_write(bd, &req_msg, sizeof(req_msg)); } #endif static int active_show(struct seq_file *s, void *unused) { int rc; unsigned long voter_mask; struct votable *v = s->private; struct battery_dbg_dev *bd = v->bd; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; seq_printf(s, "%#x\n", voter_mask); return 0; } DEFINE_SHOW_ATTRIBUTE(active); static int winvote_show(struct seq_file *s, void *unused) { int rc, winvote; unsigned long voter_mask; struct votable *v = s->private; struct battery_dbg_dev *bd = v->bd; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; winvote = voter_mask ? v->data.eff_val : -EINVAL; seq_printf(s, "%d\n", winvote); return 0; } DEFINE_SHOW_ATTRIBUTE(winvote); static int winner_show(struct seq_file *s, void *unused) { int rc; char *winner; u32 eff_voter; unsigned long voter_mask; struct votable *v = s->private; struct battery_dbg_dev *bd = v->bd; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; if (voter_mask) { eff_voter = v->data.eff_voter; winner = bd->all_data.voters[eff_voter]; } else { winner = ""; } seq_printf(s, "%s\n", winner); return 0; } DEFINE_SHOW_ATTRIBUTE(winner); static int voters_show(struct seq_file *s, void *unused) { int rc, i; unsigned long voter_mask; struct votable *v = s->private; struct battery_dbg_dev *bd = v->bd; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; for_each_set_bit(i, &voter_mask, MAX_NUM_VOTERS) seq_printf(s, "%s ", bd->all_data.voters[i]); seq_puts(s, "\n"); return 0; } DEFINE_SHOW_ATTRIBUTE(voters); static int votes_show(struct seq_file *s, void *unused) { int rc, i; unsigned long voter_mask; struct votable *v = s->private; struct battery_dbg_dev *bd = v->bd; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; for_each_set_bit(i, &voter_mask, MAX_NUM_VOTERS) seq_printf(s, "%d ", v->data.votes[i]); seq_puts(s, "\n"); return 0; } DEFINE_SHOW_ATTRIBUTE(votes); static int status_show(struct seq_file *s, void *unused) { int rc, i, winvote; char *winner; u32 eff_voter; unsigned long voter_mask; struct votable *v = s->private; struct battery_dbg_dev *bd = v->bd; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; if (!voter_mask) { seq_puts(s, "\n"); return 0; } winvote = v->data.eff_val; eff_voter = v->data.eff_voter; winner = bd->all_data.voters[eff_voter]; for_each_set_bit(i, &voter_mask, MAX_NUM_VOTERS) seq_printf(s, " %-*s: %-*s: %d\n", MAX_NAME_LEN, v->name, MAX_NAME_LEN, bd->all_data.voters[i], v->data.votes[i]); seq_printf(s, "EFFECTIVE: %-*s: %-*s: %d\n", MAX_NAME_LEN, v->name, MAX_NAME_LEN, winner, winvote); return 0; } DEFINE_SHOW_ATTRIBUTE(status); #ifdef CONFIG_QTI_PMIC_GLINK_CLIENT_DEBUG static int override_get(void *data, u64 *val) { int rc; struct votable *v = data; struct battery_dbg_dev *bd = v->bd; unsigned long voter_mask; rc = battery_dbg_request_read_votable(bd, v->id); if (rc) { pr_err("Failed to read %s votable: %d\n", v->name, rc); return rc; } voter_mask = v->data.active_voter_mask; /* Show override voter's vote only if override voter is active */ if (test_bit(bd->override_voter_id, &voter_mask)) *val = v->data.votes[bd->override_voter_id]; else *val = 0; return 0; } static int override_set(void *data, u64 val) { int rc; struct votable *v = data; struct battery_dbg_dev *bd = v->bd; u32 set = val; rc = battery_dbg_request_override(bd, v->id, set); if (rc) { pr_err("%s override request failed: %d\n", v->name, rc); return rc; } return 0; } DEFINE_DEBUGFS_ATTRIBUTE(override_fops, override_get, override_set, "%llu\n"); static int battery_dbg_create_override_file(struct votable *v, struct dentry *votable_dir) { return PTR_ERR_OR_ZERO(debugfs_create_file_unsafe("override", 0600, votable_dir, v, &override_fops)); } #else static int battery_dbg_create_override_file(struct votable *v, struct dentry *votable_dir) { return 0; } #endif static int battery_dbg_create_votable(struct battery_dbg_dev *bd, struct dentry *votables_root_dir, u32 id) { int rc; char *v_name = bd->all_data.votables[id]; struct dentry *votable_dir; votable_dir = debugfs_create_dir(v_name, votables_root_dir); if (IS_ERR(votable_dir)) { pr_err("Failed to create %s debugfs directory\n", v_name); return PTR_ERR(votable_dir); } bd->votable[id].name = v_name; rc = PTR_ERR_OR_ZERO(debugfs_create_file("active", 0400, votable_dir, &bd->votable[id], &active_fops)); if (rc) goto error; rc = PTR_ERR_OR_ZERO(debugfs_create_file("winvote", 0400, votable_dir, &bd->votable[id], &winvote_fops)); if (rc) goto error; rc = PTR_ERR_OR_ZERO(debugfs_create_file("winner", 0400, votable_dir, &bd->votable[id], &winner_fops)); if (rc) goto error; rc = PTR_ERR_OR_ZERO(debugfs_create_file("voters", 0400, votable_dir, &bd->votable[id], &voters_fops)); if (rc) goto error; rc = PTR_ERR_OR_ZERO(debugfs_create_file("votes", 0400, votable_dir, &bd->votable[id], &votes_fops)); if (rc) goto error; rc = PTR_ERR_OR_ZERO(debugfs_create_file("status", 0400, votable_dir, &bd->votable[id], &status_fops)); if (rc) goto error; rc = battery_dbg_create_override_file(&bd->votable[id], votable_dir); if (rc) goto error; return 0; error: pr_err("Failed to create debugfs file: %d\n", rc); return rc; } static int battery_dbg_get_votables_list(struct battery_dbg_dev *bd) { struct votable_req_msg req_msg = { { 0 } }; req_msg.hdr.owner = MSG_OWNER_BD; req_msg.hdr.type = MSG_TYPE_REQ_RESP; req_msg.hdr.opcode = BD_GET_AGGREGATOR_INFO_REQ; return battery_dbg_write(bd, &req_msg, sizeof(req_msg)); } static int battery_dbg_create_votables(struct battery_dbg_dev *bd, struct dentry *bd_root_dir) { int rc, id; u32 num_votables; struct dentry *votables_root_dir; votables_root_dir = debugfs_create_dir("votables", bd_root_dir); if (IS_ERR(votables_root_dir)) { pr_err("Failed to create votables root directory\n"); return PTR_ERR(votables_root_dir); } rc = battery_dbg_get_votables_list(bd); if (rc) { pr_err("Failed to get votables list: %d\n", rc); return rc; } num_votables = bd->all_data.num_votables; bd->votable = devm_kcalloc(bd->dev, num_votables, sizeof(struct votable), GFP_KERNEL); for (id = 0; id < num_votables; id++) { bd->votable[id].bd = bd; bd->votable[id].id = id; rc = battery_dbg_create_votable(bd, votables_root_dir, id); if (rc) return rc; } return 0; } static int battery_dbg_add_debugfs(struct battery_dbg_dev *bd) { int rc; struct dentry *bd_dir, *file; bd_dir = debugfs_create_dir("battery_debug", NULL); if (!bd_dir) { pr_err("Failed to create battery debugfs directory\n"); return -ENOMEM; if (IS_ERR(bd_dir)) { rc = PTR_ERR(bd_dir); pr_err("Failed to create battery debugfs directory: %d\n", rc); return rc; } file = debugfs_create_file_unsafe("get_qbg_context", 0200, bd_dir, bd, &get_qbg_context_debugfs_ops); if (!file) { pr_err("Failed to create get_qbg_context debugfs file\n"); if (IS_ERR(file)) { rc = PTR_ERR(file); pr_err("Failed to create get_qbg_context debugfs file: %d\n", rc); goto error; } bd->qbg_blob.data = bd->qbg_dump.buf; bd->qbg_blob.size = 0; file = debugfs_create_blob("qbg_context", 0444, bd_dir, &bd->qbg_blob); if (!file) { pr_err("Failed to create qbg_context debugfs file\n"); if (IS_ERR(file)) { rc = PTR_ERR(file); pr_err("Failed to create qbg_context debugfs file: %d\n", rc); goto error; } rc = battery_dbg_create_votables(bd, bd_dir); if (rc) { pr_err("Failed to create votables: %d\n", rc); goto error; } Loading @@ -156,7 +667,7 @@ static int battery_dbg_add_debugfs(struct battery_dbg_dev *bd) return 0; error: debugfs_remove_recursive(bd_dir); return -ENOMEM; return rc; } static int battery_dbg_probe(struct platform_device *pdev) Loading