msm: cache_erp: Support logging ERP events to system memory

Add the ability to log ERP events to an area of memory that
is preserved across a system reset, to aid in analyzing and
debugging ERP-related problems in the field.

Change-Id: Iff19fa06fb3f1c14339230729afe57d3f4e9ca3b
Signed-off-by: Stepan Moskovchenko <stepanm@codeaurora.org>
diff --git a/arch/arm/mach-msm/cache_erp.c b/arch/arm/mach-msm/cache_erp.c
index 97225ac..4d7ce12 100644
--- a/arch/arm/mach-msm/cache_erp.c
+++ b/arch/arm/mach-msm/cache_erp.c
@@ -35,6 +35,9 @@
 /* Print a message for everything but TLB MH events */
 #define CESR_PRINT_MASK		0x000000FF
 
+/* Log everything but TLB MH events */
+#define CESR_LOG_EVENT_MASK	0x000000FF
+
 #define L2ESR_IND_ADDR		0x204
 #define L2ESYNR0_IND_ADDR	0x208
 #define L2ESYNR1_IND_ADDR	0x209
@@ -87,6 +90,9 @@
 
 #define MODULE_NAME "msm_cache_erp"
 
+#define ERP_LOG_MAGIC_ADDR	0x748
+#define ERP_LOG_MAGIC		0x11C39893
+
 struct msm_l1_err_stats {
 	unsigned int dctpe;
 	unsigned int dcdpe;
@@ -114,6 +120,10 @@
 static int l1_erp_irq, l2_erp_irq;
 static struct proc_dir_entry *procfs_entry;
 
+#ifdef CONFIG_MSM_L1_ERR_LOG
+static struct proc_dir_entry *procfs_log_entry;
+#endif
+
 static inline unsigned int read_cesr(void)
 {
 	unsigned int cesr;
@@ -192,6 +202,48 @@
 	return len;
 }
 
+#ifdef CONFIG_MSM_L1_ERR_LOG
+static int proc_read_log(char *page, char **start, off_t off, int count,
+	int *eof, void *data)
+{
+	char *p = page;
+	int len, log_value;
+	log_value = __raw_readl(MSM_IMEM_BASE + ERP_LOG_MAGIC_ADDR) ==
+			ERP_LOG_MAGIC ? 1 : 0;
+
+	p += snprintf(p, PAGE_SIZE, "%d\n", log_value);
+
+	len = (p - page) - off;
+	if (len < 0)
+		len = 0;
+
+	*eof = (len <= count) ? 1 : 0;
+	*start = page + off;
+
+	return len;
+}
+
+static void log_cpu_event(void)
+{
+	__raw_writel(ERP_LOG_MAGIC, MSM_IMEM_BASE + ERP_LOG_MAGIC_ADDR);
+	mb();
+}
+
+static int procfs_event_log_init(void)
+{
+	procfs_log_entry = create_proc_entry("cpu/msm_erp_log", S_IRUGO, NULL);
+
+	if (!procfs_log_entry)
+		return -ENODEV;
+	procfs_log_entry->read_proc = proc_read_log;
+	return 0;
+}
+
+#else
+static inline void log_cpu_event(void) { }
+static inline int procfs_event_log_init(void) { return 0; }
+#endif
+
 static irqreturn_t msm_l1_erp_irq(int irq, void *dev_id)
 {
 	struct msm_l1_err_stats *l1_stats = dev_id;
@@ -199,6 +251,7 @@
 	unsigned int i_cesynr, d_cesynr;
 	unsigned int cpu = smp_processor_id();
 	int print_regs = cesr & CESR_PRINT_MASK;
+	int log_event = cesr & CESR_LOG_EVENT_MASK;
 
 	void *const saw_bases[] = {
 		MSM_SAW0_BASE,
@@ -271,6 +324,9 @@
 		pr_alert("D-side CESYNR = 0x%08x\n", d_cesynr);
 	}
 
+	if (log_event)
+		log_cpu_event();
+
 	/* Clear the interrupt bits we processed */
 	write_cesr(cesr);
 
@@ -459,6 +515,11 @@
 	put_online_cpus();
 
 	procfs_entry->read_proc = proc_read_status;
+
+	ret = procfs_event_log_init();
+	if (ret)
+		pr_err("Failed to create procfs node for ERP log access\n");
+
 	return 0;
 
 fail_l2: