diff options
| author | Karthikeyan Ramasubramanian <kramasub@codeaurora.org> | 2016-01-22 16:27:03 -0700 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 11:02:10 -0700 |
| commit | d4516a3652da25020efa5aad5a9eee0cf96a74e4 (patch) | |
| tree | 68035093d2747170de7617519a3b57cc9c0a9ac2 /drivers/soc/qcom/smp2p_debug.c | |
| parent | 180a1bcbe0a7516286ceecba6adb8e548ac366bc (diff) | |
soc: qcom: Add snapshot of SMP2P Driver
This snapshot is taken as of msm-3.18 commit e70ad0cd (Promotion of
kernel.lnx.3.18-151201.)
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
Diffstat (limited to 'drivers/soc/qcom/smp2p_debug.c')
| -rw-r--r-- | drivers/soc/qcom/smp2p_debug.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/drivers/soc/qcom/smp2p_debug.c b/drivers/soc/qcom/smp2p_debug.c new file mode 100644 index 000000000000..4deb05a08139 --- /dev/null +++ b/drivers/soc/qcom/smp2p_debug.c @@ -0,0 +1,335 @@ +/* drivers/soc/qcom/smp2p_debug.c + * + * Copyright (c) 2013-2014, 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 + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/ctype.h> +#include <linux/list.h> +#include <linux/debugfs.h> +#include <linux/io.h> +#include "smp2p_private.h" + +#if defined(CONFIG_DEBUG_FS) + +/** + * Dump interrupt statistics. + * + * @s: pointer to output file + */ +static void smp2p_int_stats(struct seq_file *s) +{ + struct smp2p_interrupt_config *int_cfg; + int pid; + + int_cfg = smp2p_get_interrupt_config(); + if (!int_cfg) + return; + + seq_puts(s, "| Processor | Incoming Id | Incoming # |"); + seq_puts(s, " Outgoing # | Base Ptr | Mask |\n"); + + for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) { + if (!int_cfg[pid].is_configured && + pid != SMP2P_REMOTE_MOCK_PROC) + continue; + + seq_printf(s, "| %5s (%d) | %11u | %10u | %10u | %p | %08x |\n", + int_cfg[pid].name, + pid, int_cfg[pid].in_int_id, + int_cfg[pid].in_interrupt_count, + int_cfg[pid].out_interrupt_count, + int_cfg[pid].out_int_ptr, + int_cfg[pid].out_int_mask); + } +} + +/** + * Dump item header line 1. + * + * @buf: output buffer + * @max: length of output buffer + * @item_ptr: SMEM item pointer + * @state: item state + * @returns: Number of bytes written to output buffer + */ +static int smp2p_item_header1(char *buf, int max, struct smp2p_smem *item_ptr, + enum msm_smp2p_edge_state state) +{ + int i = 0; + const char *state_text; + + if (!item_ptr) { + i += scnprintf(buf + i, max - i, "None"); + return i; + } + + switch (state) { + case SMP2P_EDGE_STATE_CLOSED: + state_text = "State: Closed"; + break; + case SMP2P_EDGE_STATE_OPENING: + state_text = "State: Opening"; + break; + case SMP2P_EDGE_STATE_OPENED: + state_text = "State: Opened"; + break; + default: + state_text = ""; + break; + } + + i += scnprintf(buf + i, max - i, + "%-14s LPID %d RPID %d", + state_text, + SMP2P_GET_LOCAL_PID(item_ptr->rem_loc_proc_id), + SMP2P_GET_REMOTE_PID(item_ptr->rem_loc_proc_id) + ); + + return i; +} + +/** + * Dump item header line 2. + * + * @buf: output buffer + * @max: length of output buffer + * @item_ptr: SMEM item pointer + * @returns: Number of bytes written to output buffer + */ +static int smp2p_item_header2(char *buf, int max, struct smp2p_smem *item_ptr) +{ + int i = 0; + + if (!item_ptr) { + i += scnprintf(buf + i, max - i, "None"); + return i; + } + + i += scnprintf(buf + i, max - i, + "Version: %08x Features: %08x", + SMP2P_GET_VERSION(item_ptr->feature_version), + SMP2P_GET_FEATURES(item_ptr->feature_version) + ); + + return i; +} + +/** + * Dump item header line 3. + * + * @buf: output buffer + * @max: length of output buffer + * @item_ptr: SMEM item pointer + * @state: item state + * @returns: Number of bytes written to output buffer + */ +static int smp2p_item_header3(char *buf, int max, struct smp2p_smem *item_ptr) +{ + int i = 0; + + if (!item_ptr) { + i += scnprintf(buf + i, max - i, "None"); + return i; + } + + i += scnprintf(buf + i, max - i, + "Entries #/Max: %d/%d Flags: %c%c", + SMP2P_GET_ENT_VALID(item_ptr->valid_total_ent), + SMP2P_GET_ENT_TOTAL(item_ptr->valid_total_ent), + item_ptr->flags & SMP2P_FLAGS_RESTART_ACK_MASK ? 'A' : 'a', + item_ptr->flags & SMP2P_FLAGS_RESTART_DONE_MASK ? 'D' : 'd' + ); + + return i; +} + +/** + * Dump individual input/output item pair. + * + * @s: pointer to output file + */ +static void smp2p_item(struct seq_file *s, int remote_pid) +{ + struct smp2p_smem *out_ptr; + struct smp2p_smem *in_ptr; + struct smp2p_interrupt_config *int_cfg; + char tmp_buff[64]; + int state; + int entry; + struct smp2p_entry_v1 *out_entries = NULL; + struct smp2p_entry_v1 *in_entries = NULL; + int out_valid = 0; + int in_valid = 0; + char entry_name[SMP2P_MAX_ENTRY_NAME]; + + int_cfg = smp2p_get_interrupt_config(); + if (!int_cfg) + return; + if (!int_cfg[remote_pid].is_configured && + remote_pid != SMP2P_REMOTE_MOCK_PROC) + return; + + out_ptr = smp2p_get_out_item(remote_pid, &state); + in_ptr = smp2p_get_in_item(remote_pid); + + if (!out_ptr && !in_ptr) + return; + + /* print item headers */ + seq_printf(s, "%s%s\n", + " ====================================== ", + "======================================"); + scnprintf(tmp_buff, sizeof(tmp_buff), + "Apps(%d)->%s(%d)", + SMP2P_APPS_PROC, int_cfg[remote_pid].name, remote_pid); + seq_printf(s, "| %-37s", tmp_buff); + + scnprintf(tmp_buff, sizeof(tmp_buff), + "%s(%d)->Apps(%d)", + int_cfg[remote_pid].name, remote_pid, SMP2P_APPS_PROC); + seq_printf(s, "| %-37s|\n", tmp_buff); + seq_printf(s, "%s%s\n", + " ====================================== ", + "======================================"); + + smp2p_item_header1(tmp_buff, sizeof(tmp_buff), out_ptr, state); + seq_printf(s, "| %-37s", tmp_buff); + smp2p_item_header1(tmp_buff, sizeof(tmp_buff), in_ptr, -1); + seq_printf(s, "| %-37s|\n", tmp_buff); + + smp2p_item_header2(tmp_buff, sizeof(tmp_buff), out_ptr); + seq_printf(s, "| %-37s", tmp_buff); + smp2p_item_header2(tmp_buff, sizeof(tmp_buff), in_ptr); + seq_printf(s, "| %-37s|\n", tmp_buff); + + smp2p_item_header3(tmp_buff, sizeof(tmp_buff), out_ptr); + seq_printf(s, "| %-37s", tmp_buff); + smp2p_item_header3(tmp_buff, sizeof(tmp_buff), in_ptr); + seq_printf(s, "| %-37s|\n", tmp_buff); + + seq_printf(s, " %s%s\n", + "-------------------------------------- ", + "--------------------------------------"); + seq_printf(s, "| %-37s", + "Entry Name Value"); + seq_printf(s, "| %-37s|\n", + "Entry Name Value"); + seq_printf(s, " %s%s\n", + "-------------------------------------- ", + "--------------------------------------"); + + /* print entries */ + if (out_ptr) { + out_entries = (struct smp2p_entry_v1 *)((void *)out_ptr + + sizeof(struct smp2p_smem)); + out_valid = SMP2P_GET_ENT_VALID(out_ptr->valid_total_ent); + } + + if (in_ptr) { + in_entries = (struct smp2p_entry_v1 *)((void *)in_ptr + + sizeof(struct smp2p_smem)); + in_valid = SMP2P_GET_ENT_VALID(in_ptr->valid_total_ent); + } + + for (entry = 0; out_entries || in_entries; ++entry) { + if (out_entries && entry < out_valid) { + memcpy_fromio(entry_name, out_entries->name, + SMP2P_MAX_ENTRY_NAME); + scnprintf(tmp_buff, sizeof(tmp_buff), + "%-16s 0x%08x", + entry_name, + out_entries->entry); + ++out_entries; + } else { + out_entries = NULL; + scnprintf(tmp_buff, sizeof(tmp_buff), "None"); + } + seq_printf(s, "| %-37s", tmp_buff); + + if (in_entries && entry < in_valid) { + memcpy_fromio(entry_name, in_entries->name, + SMP2P_MAX_ENTRY_NAME); + scnprintf(tmp_buff, sizeof(tmp_buff), + "%-16s 0x%08x", + entry_name, + in_entries->entry); + ++in_entries; + } else { + in_entries = NULL; + scnprintf(tmp_buff, sizeof(tmp_buff), "None"); + } + seq_printf(s, "| %-37s|\n", tmp_buff); + } + seq_printf(s, " %s%s\n\n", + "-------------------------------------- ", + "--------------------------------------"); +} + +/** + * Dump item state. + * + * @s: pointer to output file + */ +static void smp2p_items(struct seq_file *s) +{ + int pid; + + for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) + smp2p_item(s, pid); +} + +static struct dentry *dent; + +static int debugfs_show(struct seq_file *s, void *data) +{ + void (*show)(struct seq_file *) = s->private; + + show(s); + + return 0; +} + +static int debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, debugfs_show, inode->i_private); +} + +static const struct file_operations debug_ops = { + .open = debug_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +void debug_create(const char *name, + void (*show)(struct seq_file *)) +{ + struct dentry *file; + + file = debugfs_create_file(name, 0444, dent, show, &debug_ops); + if (!file) + pr_err("%s: unable to create file '%s'\n", __func__, name); +} + +static int __init smp2p_debugfs_init(void) +{ + dent = debugfs_create_dir("smp2p", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debug_create("int_stats", smp2p_int_stats); + debug_create("items", smp2p_items); + + return 0; +} + +late_initcall(smp2p_debugfs_init); +#endif /* CONFIG_DEBUG_FS */ |
