ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/arch/arm/mach-mmp/ramdump_util.c b/marvell/linux/arch/arm/mach-mmp/ramdump_util.c
new file mode 100644
index 0000000..deb6138
--- /dev/null
+++ b/marvell/linux/arch/arm/mach-mmp/ramdump_util.c
@@ -0,0 +1,571 @@
+/*
+ *  Copyright:  (C) Copyright 2015 Marvell International Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  publishhed by the Free Software Foundation.
+ *
+ *  Author:  Yan Markman (ymarkman@marvell.com)
+ *
+ *  Utilities for ramdump.c debug capability extension:
+ *  - Kernel-RO CRC16 check and reporting on panic
+ *  - Control /dev/ramdump_ctl for ramdump enable/disable, panic, crc check...
+ */
+#define _RAMDUMP_UTIL_C
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/crc16.h>
+#include <asm/uaccess.h>
+#include <asm-generic/sections.h>
+#include <soc/asr/addr-map.h>
+#include <linux/io.h>
+#include <linux/sched/clock.h>
+#include <soc/asr/ramdump.h>
+#include <soc/asr/ramdump_util.h>
+#include <soc/asr/ramdump_miscdevice.h>
+#include <soc/asr/regs-timers.h>
+#ifdef CONFIG_PXA_MIPSRAM
+#include <linux/mipsram.h>
+#endif
+
+/* need to make sure all kernel modules are loaded before
+* calculating the kernel CRC16 value, as kernel modules often
+* invokes static_key APIs and dirties the kernel code area
+*/
+#define RDP_CRC16_CALC_SLP_SEC (64)
+
+extern int k_signal_panic_guid_set(int set_uid, int *sig_guid_array, int size);
+
+static int sysdbg_cdev_create_register(const char *dev_name,
+	const struct file_operations *fops);
+
+#ifndef CONFIG_CRC16
+int get_kernel_text_crc16_valid(void)	{ return 1; }
+u16 get_kernel_text_crc16_on_panic(void)	{}
+int get_kernel_text_crc16_threaded_req(void) { return 0; }
+#else
+
+/* RAM consistency checker (also usable as Kernel-ID)
+ * applied over ReadOnly area - Kernel TEXT
+ * Called once on startup for "good reference" and once on panic
+ * "On panic" calculation has latency ~320mS
+ * "startup" called by late_initcall() and done over special "kcrc" kthread
+ * The CPU is 100% loaded, so delay calculation for 2sec and do it by
+ * chunks 32kB. Exit thread upon finish.
+ */
+static char *crc_txt = "KERNEL-TEXT-CRC:";
+static u16 kernel_text_crc16_buf[2];
+
+int get_kernel_text_crc16_valid(void)
+{
+	if (!kernel_text_crc16_buf[1])
+		return -2; /* not accounted */
+	if (kernel_text_crc16_buf[0] &&
+		(kernel_text_crc16_buf[0] != kernel_text_crc16_buf[1]))
+		return -1;
+	return 0;
+}
+
+/* "one-shot" blocking with very long latency */
+u16 get_kernel_text_crc16_on_panic(void)
+{
+	u16 crc;
+	/* first parameter is - previous CRC value */
+	crc = crc16(0, (u8*)&_text, (size_t)&_etext - (size_t)&_text);
+	kernel_text_crc16_buf[1] = crc;
+	pr_err("%s orig/panic = 0x%x/0x%x\n", crc_txt,
+		kernel_text_crc16_buf[0], kernel_text_crc16_buf[1]);
+	return crc;
+}
+
+/* short latency pices with sleep - to be called by a thread only */
+static int get_kernel_text_crc16_sleep(void *data)
+{
+	const int chunk = 0x8000;
+	u16 crc;
+	int len, size;
+	u8 *p;
+	int initcall_run = ((int)data == 1);
+
+	if (initcall_run) {
+		/* Kernel-RO is modified by NET-pack
+		 * Delay crc-account for 10sec
+		 */
+		ssleep(RDP_CRC16_CALC_SLP_SEC);
+	}
+
+	if (!initcall_run && !kernel_text_crc16_buf[0]) {
+		pr_debug("%s kcrc_init not finished\n", crc_txt);
+		return 0;
+	}
+
+	crc = 0;
+	p = (u8*)&_text;
+	size = (size_t)&_etext - (size_t)&_text;
+
+	do {
+		len = (size > chunk) ? chunk : size;
+		crc = crc16(crc, p, len);
+		p += len;
+		size -= len;
+		msleep(8);
+	} while (size);
+
+	if (initcall_run) {
+		/* Run on init */
+		kernel_text_crc16_buf[0] = crc;
+		pr_info("%s 0x%x\n", crc_txt, crc);
+	} else {
+		/* Run from proc command */
+		kernel_text_crc16_buf[1] = crc;
+		pr_err("%s orig/run = 0x%x/0x%x\n", crc_txt,
+			kernel_text_crc16_buf[0], kernel_text_crc16_buf[1]);
+	}
+	return 0;
+}
+
+int get_kernel_text_crc16_threaded_req(void)
+{
+	pr_info("Kernel-RO CRC checking started. Takes about 4sec...\n");
+	kthread_run(get_kernel_text_crc16_sleep, NULL, "kcrc");
+	return 0;
+}
+
+static int kernel_text_crc16_init(void)
+{
+	kthread_run(get_kernel_text_crc16_sleep, (void*)1, "kcrc_init");
+	return 0;
+}
+late_initcall(kernel_text_crc16_init);
+#endif/*CONFIG_CRC16*/
+
+/*#define KERNEL_BAD_CRC_DEBUG*/
+#ifdef KERNEL_BAD_CRC_DEBUG
+/* If Kernel CRC is wrong need to debug this,
+ * but KERNEL RO is not saved in RAMDUMP.
+ * Let's force-copy the Kernel into predefined address
+ * and panic at once
+ */
+static int get_kernel_text_copy2ddr(void)
+{
+	unsigned long flags;
+	unsigned *src = (unsigned*)&_text;
+	unsigned *dst = (unsigned*)0xC2000000;
+	unsigned size = (unsigned)&_etext - (unsigned)&_text;
+
+	pr_err("Kernel size_0x%x copy from 0x%x to 0x%x\n",
+		size, (unsigned)src, (unsigned)dst);
+	size /= sizeof(int);
+	local_irq_save(flags);
+	while (size--)
+		*dst++ = *src++;
+	panic("Kernel-Copy");
+	return 0;
+}
+#endif
+
+#if !defined(CONFIG_CPU_ASR18XX) && !defined(CONFIG_CPU_ASR1901)
+void ramdump_clock_calibration(int full_calib) {}
+#else
+#define RD_LOOPS	100000
+#define RD_LOOPS_CCNT	(RD_LOOPS * 6 + 33)
+#define RD_IRQ_CCNT	136 /* local_irq_save + restore */
+void ramdump_clock_calibration(int full_calib)
+{
+	unsigned long flags;
+	volatile unsigned cntr;
+	u64 ts_nsec0, ts_nsec1;
+	register unsigned int ccnt0, ccnt1;
+
+	if (full_calib)
+		pr_info("\n");
+	do {
+		/* Measure */
+		cntr = RD_LOOPS;
+		local_irq_save(flags);
+		ts_nsec0 = local_clock();
+		__asm__ __volatile__("mrc p15, 0, %0, c9, c13, 0" : "=r" (ccnt0));
+		if (full_calib) {
+				while(cntr--)  /**/; /*no IRQs*/
+		} else {
+			local_irq_restore(flags);
+				while(cntr--)  /**/;
+			local_irq_save(flags);
+		}
+		__asm__ __volatile__("mrc p15, 0, %0, c9, c13, 0" : "=r" (ccnt1));
+		ts_nsec1 = local_clock();
+		local_irq_restore(flags);
+		ts_nsec1 -= ts_nsec0;
+		ccnt1 -= ccnt0;
+		pr_info("Clocks %10u in %10u nsec = %dMHz\n",
+			ccnt1, (u32)ts_nsec1, (ccnt1 * 10)/((u32)ts_nsec1/100));
+	} while (full_calib--);
+	/* Known MIN job on 100.000 loops is 600.033 ccnt */
+	pr_info("CPI IDLE = %d%c\n",
+		((RD_LOOPS_CCNT * 1000) / (ccnt1 - RD_IRQ_CCNT)) / 10, '%');
+}
+#endif/*CONFIG_CPU_ASR18XX*/
+
+/* We have 2 kinds of counters 32kHz used for timestamps:
+ * - In timer hw-module (one or more, free-running or interrupt)
+ * - Common 32kHz free running counter for AP/CP/MSA processors
+ * Kernel, MIPSRAMs, diag could use different.
+ * For offline analysis need to know delta.
+ * The clocks have different PLL-source, so delta may have drift
+ */
+struct rdp_ts_desc {
+	unsigned int cntr_tmr;
+	unsigned int cntr_cmn;
+	void __iomem *va_tmr;
+	void __iomem *va_cmn;
+	unsigned int pa_tmr;
+	unsigned int pa_cmn;
+};
+
+#define PMUTMR_CR(n)	(0x28 + (n << 2))
+
+static struct rdp_ts_desc ramdump_timestamps = {
+	.pa_tmr = APB_PHYS_BASE + 0x14000 + TMR_CR(1), /*TIMER_0_1 CR*/
+#ifndef CONFIG_CPU_ASR18XX
+	.pa_cmn = APB_PHYS_BASE + 0x16000 + TMR_CR(0), /*TIMER_1_0 CR*/
+#elif defined(CONFIG_CPU_ASR1903)
+	.pa_cmn = APB_PHYS_BASE + 0x16000 + PMUTMR_CR(1), /*PMUTIMER_1 CR*/
+#else
+	.pa_cmn = APB_PHYS_BASE + 0x3A000 + TMR_CR(0), /*CP TIMER_1_0 CR*/
+#endif
+};
+
+static void rdp_ts_init(void)
+{
+	struct rdp_ts_desc *p = &ramdump_timestamps;
+	p->va_tmr = ioremap(p->pa_tmr, 0x4);
+	p->va_cmn = ioremap(p->pa_cmn, 0x4);
+	p->cntr_tmr = (p->va_tmr) ? __raw_readl(p->va_tmr) : 0;
+	p->cntr_cmn = (p->va_cmn) ? __raw_readl(p->va_cmn) : 0;
+	pr_info("TIMESTAMP 32kHz: timer-common = %u = 0x%08x - 0x%08x\n",
+		p->cntr_tmr - p->cntr_cmn, p->cntr_tmr, p->cntr_cmn);
+}
+
+static void rdp_ts_show(void)
+{
+	struct rdp_ts_desc *p = &ramdump_timestamps;
+	unsigned int tmr, cmn;
+	tmr = (p->va_tmr) ? __raw_readl(p->va_tmr) : 0;
+	cmn = (p->va_cmn) ? __raw_readl(p->va_cmn) : 0;
+	pr_info("TIMESTAMP 32kHz: timer-common = %u = 0x%08x - 0x%08x\n",
+		tmr - cmn, tmr, cmn);
+}
+
+#ifdef CONFIG_INPUT_88PM80X_ONKEY
+extern int (*pm80x_onkey_cb_ret0_to_continue)(unsigned int param);
+/* Helpfull for immediate panic and PM wake event debug */
+#ifdef CONFIG_PXA_MIPSRAM
+#define MIPS_RAM_ONKEY_TRACE() MIPS_RAM_ADD_TRACE(0x10101010) /* IO IO */
+#else
+#define MIPS_RAM_ONKEY_TRACE
+#endif
+
+static int ramdump_onkey_empty_evnt(unsigned int val)
+{
+#ifdef CONFIG_PXA_MIPSRAM
+	MIPS_RAM_ONKEY_TRACE();
+#endif
+	rdp_ts_show();
+	return -1; /* do nothing but empty event only */
+}
+static int ramdump_onkey_panic_cntr;
+static int ramdump_onkey_panic(unsigned int val)
+{
+#ifdef CONFIG_PXA_MIPSRAM
+	MIPS_RAM_ONKEY_TRACE();
+#endif
+	rdp_ts_show();
+	BUG_ON(!ramdump_onkey_panic_cntr--);
+	return -1;
+}
+#endif/*CONFIG_INPUT_88PM80X_ONKEY*/
+
+void ramdump_ignore_fatal_signals(int on_shutdown)
+{
+	/* This procedure is used on shutdown and is a "shortcut" alternative
+	 * to the "conventional" /proc/sys/kernel/print-fatal-signals
+	 * which is too long for shutdown
+	 */
+	extern int print_fatal_signals;
+	if (ramdump_level <= RAMDUMP_LEVEL_PANIC_ONLY) {
+		if (on_shutdown)
+			print_fatal_signals = 0x10;
+		else
+			print_fatal_signals = 2;
+	}
+}
+
+/*** "ramdump_ctl" - RAMDUMP Enable/Disable control device ********/
+static int ramdump_ctl_read(struct file *filp, char __user *buf,
+				size_t count, loff_t *f_pos)
+{
+	int len;
+	char kbuf[24];
+
+	if (*f_pos)
+		return 0; /* second entry of same command */
+
+	/* Could be called as "cat /dev" or as read(1byte) from SW-code
+	 * The output is different for these cases:
+	 * - read(1byte) expects Row-Binary 1 byte
+	 *  (if ramdump_level<2 the User-Space-reader ignores error)
+	 * - shell/system() read has count=4kB and obtains string
+	 */
+	if (count == 1) {
+		len = count;
+		kbuf[0] = ramdump_level & 0xff;
+	} else {
+		len = sprintf(kbuf, "\t ramdump_enable=%d\n", ramdump_level);
+	}
+	*f_pos = len;
+	if (copy_to_user(buf, kbuf, len))
+		return -EFAULT;
+	return len;
+}
+
+static const char *tstPanic = "Force-Emulate Kernel panic";
+
+static int ramdump_ctl_write(struct file *filp, const char __user *buf,
+				size_t count, loff_t *f_pos)
+{
+	char in;
+	char desc_buf[80];
+	int copy_done, copy_left, i;
+
+	if ((count <= 0) || *f_pos)
+		return 0;
+	if (copy_from_user(&in, buf, 1))
+		return -EFAULT;
+	if (in == 'd') {
+		ramdump_level = 0;
+		ramdump_ignore_fatal_signals(0);
+	} else if (in == 'e') {
+		ramdump_level = RAMDUMP_LEVEL_FULL;
+	} else if (in == 'a') {
+		ramdump_level = RAMDUMP_LEVEL_FULL_IN_ADVANCE;
+		ramdump_prepare_in_advance();
+	} else if (in == 'p') {
+		ramdump_level = 0xf;
+		desc_buf[0] = 0;
+		if (!copy_from_user(&in, &buf[1], 1)) {
+			if (in == '_') {
+				copy_left = copy_from_user(desc_buf, &buf[2], 79);
+				copy_done = 79 - copy_left;
+				desc_buf[copy_done] = 0;
+				/* strip LF */
+				i = 0;
+				while (i < copy_done) {
+					if (desc_buf[i] == '\n')
+						desc_buf[i] = 0;
+					if (!desc_buf[i++])
+						break;
+				}
+			}
+		}
+		if (desc_buf[0]) {
+			panic("%s \'%s\'\n", tstPanic, desc_buf);
+		} else {
+			panic("%s\n", tstPanic);
+		}
+	} else if (in == 'B') {
+		ramdump_level = 0xB;
+		pr_err("\n %s (BUG)\n", tstPanic);
+		BUG();
+
+	} else if (in == 'c') {
+		/* CRC blocking account with long latency! */
+		get_kernel_text_crc16_on_panic();
+		i = get_kernel_text_crc16_valid();
+		if (i == -1) {
+			pr_err("!Bad Kernel CRC!\n");
+			return -ERANGE;
+		} else if (i == -2) {
+			pr_err("CRC not accounted\n");
+			return -EAGAIN;
+		}
+#ifdef KERNEL_BAD_CRC_DEBUG
+	} else if (in == 'C') {
+		get_kernel_text_copy2ddr();
+#endif
+	/* Frequency/Clock/CPU-IDLE */
+	} else if (in == 'i') {
+		ramdump_clock_calibration(0);
+	} else if (in == 'f') {
+		ramdump_clock_calibration(1);
+
+	} else if (in == 'j') {
+		pr_info("jiffies = %lu\n", jiffies);
+
+#ifdef CONFIG_INPUT_88PM80X_ONKEY
+	} else if (in == 'E') {
+		pr_info("OnKey DEBUG = EmptyEvent\n");
+		pm80x_onkey_cb_ret0_to_continue = ramdump_onkey_empty_evnt;
+	} else if (in == 'F') {
+		pr_info("OnKey DEBUG = Panic on First\n");
+		ramdump_onkey_panic_cntr = 0;
+		pm80x_onkey_cb_ret0_to_continue = ramdump_onkey_panic;
+	} else if (in == 'S') {
+		pr_info("OnKey DEBUG = Panic on Second\n");
+		ramdump_onkey_panic_cntr = 1;
+		pm80x_onkey_cb_ret0_to_continue = ramdump_onkey_panic;
+#endif
+
+	} else if ((in == 'g') || (in == 'u')) {
+		#define K_SIG_GUIDS_MAX	7 /*in signal.c, including 0=root */
+		int num, guid[K_SIG_GUIDS_MAX+1];
+		copy_left = copy_from_user(desc_buf, &buf[1], 79);
+		copy_done = 79 - copy_left;
+		desc_buf[79] = 0;
+		/* Set all entries to "-1", plus 1 extra to check input NUM */
+		memset(guid, 0xFF, sizeof(guid));
+		num = sscanf(desc_buf, "id=%d,%d,%d,%d,%d,%d,%d,%d",
+			&guid[0], &guid[1], &guid[2], &guid[3],
+			&guid[4], &guid[5], &guid[6], &guid[7]);
+		if ((num <= 0) || (num > K_SIG_GUIDS_MAX))
+			return -EIO;
+		if (k_signal_panic_guid_set((in == 'u'), guid, num) != num)
+			return -EIO;
+
+	} else if ((in >= '0') && (in <= '9')) {
+		ramdump_level = (unsigned)(in - '0');
+		ramdump_ignore_fatal_signals(0);/*level-check is inside*/
+
+	} else {
+		pr_info("ramdump_ctl: options\n"
+			"   0/1=e/d, p[anic], B[UG], a[dvance]\n"
+			"   gid=1,2,3,4  or  uid=1,2,3,4  set guid for panic on fatal-signals\n"
+			"   j[iffies], i[IDLE], f[frequency]\n"
+			"   c[crc check]\n"
+#ifdef CONFIG_INPUT_88PM80X_ONKEY
+			"   OnKey: E[EmptyEvnt] F[FirstPanic] S[SecondPanic]\n"
+#endif
+			"\n");
+		return -EINVAL;
+	}
+	*f_pos = count;
+	return count;
+}
+
+static const struct file_operations ramdump_ctl_fops = {
+	.owner = THIS_MODULE,
+	.read = ramdump_ctl_read,
+	.write = ramdump_ctl_write,
+};
+
+static int __init ramdump_ctl_init(void)
+{
+	rdp_ts_init();
+	/* Create Register sysdbg-node (not a MISC) device */
+	return
+		sysdbg_cdev_create_register("ramdump_ctl", &ramdump_ctl_fops);
+}
+late_initcall(ramdump_ctl_init);
+
+
+
+/****************************************************************************
+ * RAMDUMP Devices are for System Debug ErrorHandling and Health Management
+ * Working MISC-Devices may be stack whilst working but RAMDUMP-devices
+ * must be always ok to guaranty system recovery and logging.
+ *
+ * Do not use MISC-Device for RAMDUMPs but use own
+ * Major-Node-Number for them. The devices are still "char"
+ */
+static int sysdbg_major;
+static int sysdbg_minor = -1;
+static char *sysdbg_class_name = (char*)"sysdbg";
+static struct class *sysdbg_class;
+
+static int sysdbg_cdev_create_register(const char *dev_name,
+	const struct file_operations *fops)
+{
+	struct cdev *cdev;
+	int err = 0;
+	dev_t dev = 0;
+	struct device *pdev;
+
+	if (!sysdbg_class)
+		sysdbg_class = class_create(THIS_MODULE, sysdbg_class_name);
+
+	sysdbg_minor++;
+
+	if (sysdbg_major) {
+		dev = MKDEV(sysdbg_major, sysdbg_minor);
+		err = register_chrdev_region(dev, 1, sysdbg_class_name);
+	} else {
+		err = alloc_chrdev_region(&dev, sysdbg_minor, 1, sysdbg_class_name);
+		sysdbg_major = MAJOR(dev);
+	}
+	if (err < 0) {
+		pr_err("%s:%s can't get major %d\n", sysdbg_class_name, dev_name,
+			sysdbg_major);
+		goto fail_1;
+	}
+	cdev = cdev_alloc();
+	if (!cdev) {
+		err = -ENOMEM;
+		goto fail_2;
+	}
+	dev = MKDEV(sysdbg_major, sysdbg_minor);
+	cdev->ops = fops;
+	cdev->owner = THIS_MODULE;
+	err = cdev_add(cdev, dev, 1);
+	if (!err) {
+		pdev = device_create(sysdbg_class, NULL, dev, NULL, dev_name);
+		if (pdev)
+			return 0;
+	}
+
+	/* ----- FAIL ------------------- */
+	/*device_destroy(sysdbg_class, devno) -- nothing to destroy*/
+	cdev_del(cdev);
+fail_2:
+	unregister_chrdev_region(dev, 1);
+	pr_err("%s:%s cdev err %d\n", sysdbg_class_name, dev_name, err);
+fail_1:
+	sysdbg_minor--;
+	/*class_destroy(sysdbg_class) - never destroy this class*/
+	return err;
+}
+
+/* GLOBAL alternator/wrappers */
+int sysdbg_misc_register(struct miscdevice * misc)
+{
+	return sysdbg_cdev_create_register(misc->name, misc->fops);
+}
+
+int sysdbg_misc_deregister(struct miscdevice * misc)
+{
+	pr_err("Class %s device %s shuld never be deleted\n",
+		sysdbg_class_name, misc->name);
+	return 0;
+}
+
+static int (*ramdump_seh_callback)(char *buf, int len);
+void ramdump_seh_callback_bind(void *send_msg_callback)
+{
+	ramdump_seh_callback = send_msg_callback;
+}
+EXPORT_SYMBOL(ramdump_seh_callback_bind);
+
+int ramdump_send_msg_to_seh (char *buf, int len)
+{
+	if (!ramdump_seh_callback)
+		return -1;
+	return /* num-sent bytes > 0 are valid */
+		ramdump_seh_callback(buf, len);
+}
+EXPORT_SYMBOL(ramdump_send_msg_to_seh);
+