msm: smd: Add interrupt debugging support
Add counters for tracking interrupts. This helps for
debugging interrupt issues and provides a centralized
place to verify that SMD interrupts are working
correctly.
This can be used as an additional verification
tool when moving interrupt configuration into
platform data and device tree.
Change-Id: Ie3936abc59f0d9a470e96a2f5da4b0390d7f7de1
Signed-off-by: Eric Holmberg <eholmber@codeaurora.org>
diff --git a/arch/arm/mach-msm/include/mach/msm_smd.h b/arch/arm/mach-msm/include/mach/msm_smd.h
index d42cd40..8d3e640 100644
--- a/arch/arm/mach-msm/include/mach/msm_smd.h
+++ b/arch/arm/mach-msm/include/mach/msm_smd.h
@@ -244,6 +244,14 @@
*/
const char *smd_edge_to_subsystem(uint32_t type);
+/*
+ * Returns a pointer to the subsystem name given the
+ * remote processor ID.
+ *
+ * @pid Remote processor ID
+ * @returns Pointer to subsystem name or NULL if not found
+ */
+const char *smd_pid_to_subsystem(uint32_t pid);
#else
static inline int smd_open(const char *name, smd_channel_t **ch, void *priv,
@@ -350,6 +358,11 @@
{
return NULL;
}
+
+static inline const char *smd_pid_to_subsystem(uint32_t pid)
+{
+ return NULL;
+}
#endif
#endif
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index 37c8920..f9917d3 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -145,6 +145,7 @@
.smsm.irq_handler = smsm_wcnss_irq_handler,
},
};
+struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
#define SMSM_STATE_ADDR(entry) (smsm_info.state + entry)
#define SMSM_INTR_MASK_ADDR(entry, host) (smsm_info.intr_mask + \
@@ -377,33 +378,42 @@
{
static const struct interrupt_config_item *intr
= &private_intr_config[SMD_MODEM].smd;
- if (intr->out_base)
+ if (intr->out_base) {
+ ++interrupt_stats[SMD_MODEM].smd_out_config_count;
smd_write_intr(intr->out_bit_pos,
intr->out_base + intr->out_offset);
- else
+ } else {
+ ++interrupt_stats[SMD_MODEM].smd_out_hardcode_count;
MSM_TRIG_A2M_SMD_INT;
+ }
}
static inline void notify_dsp_smd(void)
{
static const struct interrupt_config_item *intr
= &private_intr_config[SMD_Q6].smd;
- if (intr->out_base)
+ if (intr->out_base) {
+ ++interrupt_stats[SMD_Q6].smd_out_config_count;
smd_write_intr(intr->out_bit_pos,
intr->out_base + intr->out_offset);
- else
+ } else {
+ ++interrupt_stats[SMD_Q6].smd_out_hardcode_count;
MSM_TRIG_A2Q6_SMD_INT;
+ }
}
static inline void notify_dsps_smd(void)
{
static const struct interrupt_config_item *intr
= &private_intr_config[SMD_DSPS].smd;
- if (intr->out_base)
+ if (intr->out_base) {
+ ++interrupt_stats[SMD_DSPS].smd_out_config_count;
smd_write_intr(intr->out_bit_pos,
intr->out_base + intr->out_offset);
- else
+ } else {
+ ++interrupt_stats[SMD_DSPS].smd_out_hardcode_count;
MSM_TRIG_A2DSPS_SMD_INT;
+ }
}
static inline void notify_wcnss_smd(void)
@@ -412,44 +422,56 @@
= &private_intr_config[SMD_WCNSS].smd;
wakeup_v1_riva();
- if (intr->out_base)
+ if (intr->out_base) {
+ ++interrupt_stats[SMD_WCNSS].smd_out_config_count;
smd_write_intr(intr->out_bit_pos,
intr->out_base + intr->out_offset);
- else
+ } else {
+ ++interrupt_stats[SMD_WCNSS].smd_out_hardcode_count;
MSM_TRIG_A2WCNSS_SMD_INT;
+ }
}
static inline void notify_modem_smsm(void)
{
static const struct interrupt_config_item *intr
= &private_intr_config[SMD_MODEM].smsm;
- if (intr->out_base)
+ if (intr->out_base) {
+ ++interrupt_stats[SMD_MODEM].smsm_out_config_count;
smd_write_intr(intr->out_bit_pos,
intr->out_base + intr->out_offset);
- else
+ } else {
+ ++interrupt_stats[SMD_MODEM].smsm_out_hardcode_count;
MSM_TRIG_A2M_SMSM_INT;
+ }
}
static inline void notify_dsp_smsm(void)
{
static const struct interrupt_config_item *intr
= &private_intr_config[SMD_Q6].smsm;
- if (intr->out_base)
+ if (intr->out_base) {
+ ++interrupt_stats[SMD_Q6].smsm_out_config_count;
smd_write_intr(intr->out_bit_pos,
intr->out_base + intr->out_offset);
- else
+ } else {
+ ++interrupt_stats[SMD_Q6].smsm_out_hardcode_count;
MSM_TRIG_A2Q6_SMSM_INT;
+ }
}
static inline void notify_dsps_smsm(void)
{
static const struct interrupt_config_item *intr
= &private_intr_config[SMD_DSPS].smsm;
- if (intr->out_base)
+ if (intr->out_base) {
+ ++interrupt_stats[SMD_DSPS].smsm_out_config_count;
smd_write_intr(intr->out_bit_pos,
intr->out_base + intr->out_offset);
- else
+ } else {
+ ++interrupt_stats[SMD_DSPS].smsm_out_hardcode_count;
MSM_TRIG_A2DSPS_SMSM_INT;
+ }
}
static inline void notify_wcnss_smsm(void)
@@ -458,11 +480,14 @@
= &private_intr_config[SMD_WCNSS].smsm;
wakeup_v1_riva();
- if (intr->out_base)
+ if (intr->out_base) {
+ ++interrupt_stats[SMD_WCNSS].smsm_out_config_count;
smd_write_intr(intr->out_bit_pos,
intr->out_base + intr->out_offset);
- else
+ } else {
+ ++interrupt_stats[SMD_WCNSS].smsm_out_hardcode_count;
MSM_TRIG_A2WCNSS_SMSM_INT;
+ }
}
static void notify_other_smsm(uint32_t smsm_entry, uint32_t notify_mask)
@@ -769,6 +794,30 @@
}
EXPORT_SYMBOL(smd_edge_to_subsystem);
+/*
+ * Returns a pointer to the subsystem name given the
+ * remote processor ID.
+ *
+ * @pid Remote processor ID
+ * @returns Pointer to subsystem name or NULL if not found
+ */
+const char *smd_pid_to_subsystem(uint32_t pid)
+{
+ const char *subsys = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(edge_to_pids); ++i) {
+ if (pid == edge_to_pids[i].remote_pid &&
+ edge_to_pids[i].subsys_name[0] != 0x0
+ ) {
+ subsys = edge_to_pids[i].subsys_name;
+ break;
+ }
+ }
+
+ return subsys;
+}
+EXPORT_SYMBOL(smd_pid_to_subsystem);
static void smd_reset_edge(struct smd_half_channel *ch, unsigned new_state)
{
@@ -1185,6 +1234,7 @@
static irqreturn_t smd_modem_irq_handler(int irq, void *data)
{
SMx_POWER_INFO("SMD Int Modem->Apps\n");
+ ++interrupt_stats[SMD_MODEM].smd_in_count;
handle_smd_irq(&smd_ch_list_modem, notify_modem_smd);
handle_smd_irq_closing_list();
return IRQ_HANDLED;
@@ -1193,6 +1243,7 @@
static irqreturn_t smd_dsp_irq_handler(int irq, void *data)
{
SMx_POWER_INFO("SMD Int LPASS->Apps\n");
+ ++interrupt_stats[SMD_Q6].smd_in_count;
handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd);
handle_smd_irq_closing_list();
return IRQ_HANDLED;
@@ -1201,6 +1252,7 @@
static irqreturn_t smd_dsps_irq_handler(int irq, void *data)
{
SMx_POWER_INFO("SMD Int DSPS->Apps\n");
+ ++interrupt_stats[SMD_DSPS].smd_in_count;
handle_smd_irq(&smd_ch_list_dsps, notify_dsps_smd);
handle_smd_irq_closing_list();
return IRQ_HANDLED;
@@ -1209,6 +1261,7 @@
static irqreturn_t smd_wcnss_irq_handler(int irq, void *data)
{
SMx_POWER_INFO("SMD Int WCNSS->Apps\n");
+ ++interrupt_stats[SMD_WCNSS].smd_in_count;
handle_smd_irq(&smd_ch_list_wcnss, notify_wcnss_smd);
handle_smd_irq_closing_list();
return IRQ_HANDLED;
@@ -2351,24 +2404,28 @@
static irqreturn_t smsm_modem_irq_handler(int irq, void *data)
{
SMx_POWER_INFO("SMSM Int Modem->Apps\n");
+ ++interrupt_stats[SMD_MODEM].smsm_in_count;
return smsm_irq_handler(irq, data);
}
static irqreturn_t smsm_dsp_irq_handler(int irq, void *data)
{
SMx_POWER_INFO("SMSM Int LPASS->Apps\n");
+ ++interrupt_stats[SMD_Q6].smsm_in_count;
return smsm_irq_handler(irq, data);
}
static irqreturn_t smsm_dsps_irq_handler(int irq, void *data)
{
SMx_POWER_INFO("SMSM Int DSPS->Apps\n");
+ ++interrupt_stats[SMD_DSPS].smsm_in_count;
return smsm_irq_handler(irq, data);
}
static irqreturn_t smsm_wcnss_irq_handler(int irq, void *data)
{
SMx_POWER_INFO("SMSM Int WCNSS->Apps\n");
+ ++interrupt_stats[SMD_WCNSS].smsm_in_count;
return smsm_irq_handler(irq, data);
}
diff --git a/arch/arm/mach-msm/smd_debug.c b/arch/arm/mach-msm/smd_debug.c
index b95a35c..764102d 100644
--- a/arch/arm/mach-msm/smd_debug.c
+++ b/arch/arm/mach-msm/smd_debug.c
@@ -95,6 +95,61 @@
return max;
}
+static int debug_int_stats(char *buf, int max)
+{
+ int i = 0;
+ int subsys;
+ struct interrupt_stat *stats = interrupt_stats;
+ const char *subsys_name;
+
+ i += scnprintf(buf + i, max - i,
+ " Subsystem | In | Out (Hardcoded) |"
+ " Out (Configured) |\n");
+
+ for (subsys = 0; subsys < NUM_SMD_SUBSYSTEMS; ++subsys) {
+ subsys_name = smd_pid_to_subsystem(subsys);
+ if (subsys_name) {
+ i += scnprintf(buf + i, max - i,
+ "%-10s %4s | %9u | %9u | %9u |\n",
+ smd_pid_to_subsystem(subsys), "smd",
+ stats->smd_in_count,
+ stats->smd_out_hardcode_count,
+ stats->smd_out_config_count);
+
+ i += scnprintf(buf + i, max - i,
+ "%-10s %4s | %9u | %9u | %9u |\n",
+ smd_pid_to_subsystem(subsys), "smsm",
+ stats->smsm_in_count,
+ stats->smsm_out_hardcode_count,
+ stats->smsm_out_config_count);
+ }
+ ++stats;
+ }
+
+ return i;
+}
+
+static int debug_int_stats_reset(char *buf, int max)
+{
+ int i = 0;
+ int subsys;
+ struct interrupt_stat *stats = interrupt_stats;
+
+ i += scnprintf(buf + i, max - i, "Resetting interrupt stats.\n");
+
+ for (subsys = 0; subsys < NUM_SMD_SUBSYSTEMS; ++subsys) {
+ stats->smd_in_count = 0;
+ stats->smd_out_hardcode_count = 0;
+ stats->smd_out_config_count = 0;
+ stats->smsm_in_count = 0;
+ stats->smsm_out_hardcode_count = 0;
+ stats->smsm_out_config_count = 0;
+ ++stats;
+ }
+
+ return i;
+}
+
static int debug_diag(char *buf, int max)
{
int i = 0;
@@ -694,6 +749,8 @@
debug_create("modem_err_f3", 0444, dent, debug_modem_err_f3);
debug_create("print_diag", 0444, dent, debug_diag);
debug_create("print_f3", 0444, dent, debug_f3);
+ debug_create("int_stats", 0444, dent, debug_int_stats);
+ debug_create("int_stats_reset", 0444, dent, debug_int_stats_reset);
/* NNV: this is google only stuff */
debug_create("build", 0444, dent, debug_read_build_id);
diff --git a/arch/arm/mach-msm/smd_private.h b/arch/arm/mach-msm/smd_private.h
index a4c60e8..e39c57b 100644
--- a/arch/arm/mach-msm/smd_private.h
+++ b/arch/arm/mach-msm/smd_private.h
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/smd_private.h
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -19,6 +19,7 @@
#include <linux/types.h>
#include <linux/spinlock.h>
#include <mach/msm_smsm.h>
+#include <mach/msm_smd.h>
#define PC_APPS 0
#define PC_MODEM 1
@@ -208,4 +209,15 @@
void smd_diag(void);
+struct interrupt_stat {
+ uint32_t smd_in_count;
+ uint32_t smd_out_hardcode_count;
+ uint32_t smd_out_config_count;
+
+ uint32_t smsm_in_count;
+ uint32_t smsm_out_hardcode_count;
+ uint32_t smsm_out_config_count;
+};
+extern struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
+
#endif