Merge "msm: boot markers: Add boot markers driver"
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 9583336..4bb66b4 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -240,6 +240,16 @@
 	  This figures are reported in mpm sleep clock cycles and have a
 	  resolution of 31 bits as 1 bit is used as an overflow check.
 
+config MSM_BOOT_TIME_MARKER
+        bool "Use MSM boot time marker reporting"
+        depends on MSM_BOOT_STATS
+        help
+         Use this to mark msm boot kpi for measurement.
+         An instrumentation for boot time measurement.
+         To create an entry, call "place_marker" function.
+         At userspace, write marker name to "/sys/kernel/debug/bootkpi/kpi_values"
+         If unsure, say N
+
 config MSM_CORE_HANG_DETECT
        tristate "MSM Core Hang Detection Support"
        help
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 2e59e77..64d4233 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -26,6 +26,7 @@
 obj-$(CONFIG_QCOM_EARLY_RANDOM)	+= early_random.o
 obj-$(CONFIG_SOC_BUS) += socinfo.o
 obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o
+obj-$(CONFIG_MSM_BOOT_TIME_MARKER) += boot_marker.o
 obj-$(CONFIG_MSM_CORE_HANG_DETECT) += core_hang_detect.o
 obj-$(CONFIG_MSM_GLADIATOR_HANG_DETECT) += gladiator_hang_detect.o
 obj-$(CONFIG_MSM_GLADIATOR_ERP) += gladiator_erp.o
diff --git a/drivers/soc/qcom/boot_marker.c b/drivers/soc/qcom/boot_marker.c
new file mode 100644
index 0000000..b7a442c
--- /dev/null
+++ b/drivers/soc/qcom/boot_marker.c
@@ -0,0 +1,238 @@
+/* Copyright (c) 2016, 2019, 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/kernel.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <soc/qcom/boot_stats.h>
+
+#define MAX_STRING_LEN 256
+#define BOOT_MARKER_MAX_LEN 40
+
+struct boot_marker {
+	char marker_name[BOOT_MARKER_MAX_LEN];
+	unsigned long long int timer_value;
+	struct list_head list;
+	struct mutex lock;
+};
+
+static struct dentry *dent_bkpi, *dent_bkpi_status, *dent_mpm_timer;
+static struct boot_marker boot_marker_list;
+
+static void _create_boot_marker(const char *name,
+		unsigned long long int timer_value)
+{
+	struct boot_marker *new_boot_marker;
+
+	pr_debug("%-41s:%llu.%03llu seconds\n", name,
+			timer_value/TIMER_KHZ,
+			((timer_value % TIMER_KHZ)
+			 * 1000) / TIMER_KHZ);
+
+	new_boot_marker = kmalloc(sizeof(*new_boot_marker), GFP_KERNEL);
+	if (!new_boot_marker)
+		return;
+
+	strlcpy(new_boot_marker->marker_name, name,
+			sizeof(new_boot_marker->marker_name));
+	new_boot_marker->timer_value = timer_value;
+
+	mutex_lock(&boot_marker_list.lock);
+	list_add_tail(&(new_boot_marker->list), &(boot_marker_list.list));
+	mutex_unlock(&boot_marker_list.lock);
+}
+
+static void set_bootloader_stats(void)
+{
+	_create_boot_marker("M - APPSBL Start - ",
+		readl_relaxed(&boot_stats->bootloader_start));
+	_create_boot_marker("M - APPSBL Display Init - ",
+		readl_relaxed(&boot_stats->bootloader_display));
+	_create_boot_marker("M - APPSBL Early-Domain Start - ",
+		readl_relaxed(&boot_stats->bootloader_early_domain_start));
+	_create_boot_marker("D - APPSBL Kernel Load Time - ",
+		readl_relaxed(&boot_stats->bootloader_load_kernel));
+	_create_boot_marker("D - APPSBL Kernel Auth Time - ",
+		readl_relaxed(&boot_stats->bootloader_checksum));
+	_create_boot_marker("M - APPSBL End - ",
+		readl_relaxed(&boot_stats->bootloader_end));
+}
+
+void place_marker(const char *name)
+{
+	_create_boot_marker((char *) name, msm_timer_get_sclk_ticks());
+}
+EXPORT_SYMBOL(place_marker);
+
+static ssize_t bootkpi_reader(struct file *fp, char __user *user_buffer,
+		size_t count, loff_t *position)
+{
+	int rc = 0;
+	char *buf;
+	int temp = 0;
+	struct boot_marker *marker;
+
+	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	mutex_lock(&boot_marker_list.lock);
+	list_for_each_entry(marker, &boot_marker_list.list, list) {
+		temp += scnprintf(buf + temp, PAGE_SIZE - temp,
+				"%-41s:%llu.%03llu seconds\n",
+				marker->marker_name,
+				marker->timer_value/TIMER_KHZ,
+				(((marker->timer_value % TIMER_KHZ)
+				  * 1000) / TIMER_KHZ));
+	}
+	mutex_unlock(&boot_marker_list.lock);
+	rc = simple_read_from_buffer(user_buffer, count, position, buf, temp);
+	kfree(buf);
+	return rc;
+}
+
+static ssize_t bootkpi_writer(struct file *fp, const char __user *user_buffer,
+		size_t count, loff_t *position)
+{
+	int rc = 0;
+	char buf[MAX_STRING_LEN];
+
+	if (count > MAX_STRING_LEN)
+		return -EINVAL;
+	rc = simple_write_to_buffer(buf,
+			sizeof(buf) - 1, position, user_buffer, count);
+	if (rc < 0)
+		return rc;
+	buf[rc] = '\0';
+	place_marker(buf);
+	return rc;
+}
+
+static int bootkpi_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static const struct file_operations fops_bkpi = {
+	.owner = THIS_MODULE,
+	.open  = bootkpi_open,
+	.read  = bootkpi_reader,
+	.write = bootkpi_writer,
+};
+
+static ssize_t mpm_timer_read(struct file *fp, char __user *user_buffer,
+		size_t count, loff_t *position)
+{
+	unsigned long long int timer_value;
+	int rc = 0;
+	char buf[100];
+	int temp = 0;
+
+	timer_value = msm_timer_get_sclk_ticks();
+
+	temp = scnprintf(buf, sizeof(buf), "%llu.%03llu seconds\n",
+			timer_value/TIMER_KHZ,
+			(((timer_value % TIMER_KHZ) * 1000) / TIMER_KHZ));
+
+	rc = simple_read_from_buffer(user_buffer, count, position, buf, temp);
+
+	return rc;
+}
+
+static int mpm_timer_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static int mpm_timer_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	phys_addr_t addr = msm_timer_get_pa();
+
+	if (vma->vm_flags & VM_WRITE)
+		return -EPERM;
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	return vm_iomap_memory(vma, addr, PAGE_SIZE);
+}
+
+static const struct file_operations fops_mpm_timer = {
+	.owner = THIS_MODULE,
+	.open  = mpm_timer_open,
+	.read  = mpm_timer_read,
+	.mmap = mpm_timer_mmap,
+};
+
+static int __init init_bootkpi(void)
+{
+	dent_bkpi = debugfs_create_dir("bootkpi", NULL);
+	if (IS_ERR_OR_NULL(dent_bkpi))
+		return -ENODEV;
+
+	dent_bkpi_status = debugfs_create_file_unsafe("kpi_values",
+			0666, dent_bkpi, NULL, &fops_bkpi);
+	if (IS_ERR_OR_NULL(dent_bkpi_status)) {
+		debugfs_remove(dent_bkpi);
+		dent_bkpi = NULL;
+		pr_err("boot_marker: Could not create 'kpi_values' debugfs file\n");
+		return -ENODEV;
+	}
+
+	dent_mpm_timer = debugfs_create_file("mpm_timer",
+			0444, dent_bkpi, NULL, &fops_mpm_timer);
+	if (IS_ERR_OR_NULL(dent_mpm_timer)) {
+		debugfs_remove(dent_bkpi_status);
+		dent_bkpi_status = NULL;
+		debugfs_remove(dent_bkpi);
+		dent_bkpi = NULL;
+		pr_err("boot_marker: Could not create 'mpm_timer' debugfs file\n");
+		return -ENODEV;
+	}
+
+	INIT_LIST_HEAD(&boot_marker_list.list);
+	mutex_init(&boot_marker_list.lock);
+	set_bootloader_stats();
+	return 0;
+}
+subsys_initcall(init_bootkpi);
+
+static void __exit exit_bootkpi(void)
+{
+	struct boot_marker *marker;
+	struct boot_marker *temp_addr;
+
+	debugfs_remove_recursive(dent_bkpi);
+	mutex_lock(&boot_marker_list.lock);
+	list_for_each_entry_safe(marker, temp_addr, &boot_marker_list.list,
+			list) {
+		list_del(&marker->list);
+		kfree(marker);
+	}
+	mutex_unlock(&boot_marker_list.lock);
+	boot_stats_exit();
+}
+module_exit(exit_bootkpi);
+
+MODULE_DESCRIPTION("MSM boot key performance indicators");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/boot_stats.c b/drivers/soc/qcom/boot_stats.c
index 2fc9cbf..d9f36aa 100644
--- a/drivers/soc/qcom/boot_stats.c
+++ b/drivers/soc/qcom/boot_stats.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014,2019 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
@@ -22,17 +22,12 @@
 #include <linux/sched.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
-
-struct boot_stats {
-	uint32_t bootloader_start;
-	uint32_t bootloader_end;
-	uint32_t bootloader_display;
-	uint32_t bootloader_load_kernel;
-};
+#include <soc/qcom/boot_stats.h>
 
 static void __iomem *mpm_counter_base;
+static phys_addr_t mpm_counter_pa;
 static uint32_t mpm_counter_freq;
-static struct boot_stats __iomem *boot_stats;
+struct boot_stats __iomem *boot_stats;
 
 static int mpm_parse_dt(void)
 {
@@ -75,17 +70,58 @@
 static void print_boot_stats(void)
 {
 	pr_info("KPI: Bootloader start count = %u\n",
-		readl_relaxed(&boot_stats->bootloader_start));
+			readl_relaxed(&boot_stats->bootloader_start));
 	pr_info("KPI: Bootloader end count = %u\n",
-		readl_relaxed(&boot_stats->bootloader_end));
+			readl_relaxed(&boot_stats->bootloader_end));
 	pr_info("KPI: Bootloader display count = %u\n",
-		readl_relaxed(&boot_stats->bootloader_display));
+			readl_relaxed(&boot_stats->bootloader_display));
 	pr_info("KPI: Bootloader load kernel count = %u\n",
-		readl_relaxed(&boot_stats->bootloader_load_kernel));
+			readl_relaxed(&boot_stats->bootloader_load_kernel));
 	pr_info("KPI: Kernel MPM timestamp = %u\n",
-		readl_relaxed(mpm_counter_base));
+			readl_relaxed(mpm_counter_base));
 	pr_info("KPI: Kernel MPM Clock frequency = %u\n",
-		mpm_counter_freq);
+			mpm_counter_freq);
+}
+
+unsigned long long int msm_timer_get_sclk_ticks(void)
+{
+	unsigned long long int t1, t2;
+	int loop_count = 10;
+	int loop_zero_count = 3;
+	u64 tmp = USEC_PER_SEC;
+	void __iomem *sclk_tick;
+
+	do_div(tmp, TIMER_KHZ);
+	tmp /= (loop_zero_count-1);
+	sclk_tick = mpm_counter_base;
+	if (!sclk_tick)
+		return -EINVAL;
+	while (loop_zero_count--) {
+		t1 = __raw_readl_no_log(sclk_tick);
+		do {
+			udelay(1);
+			t2 = t1;
+			t1 = __raw_readl_no_log(sclk_tick);
+		} while ((t2 != t1) && --loop_count);
+		if (!loop_count) {
+			pr_err("boot_stats: SCLK  did not stabilize\n");
+			return 0;
+		}
+		if (t1)
+			break;
+
+		udelay(tmp);
+	}
+	if (!loop_zero_count) {
+		pr_err("boot_stats: SCLK reads zero\n");
+		return 0;
+	}
+	return t1;
+}
+
+phys_addr_t msm_timer_get_pa(void)
+{
+	return mpm_counter_pa;
 }
 
 int boot_stats_init(void)
@@ -98,9 +134,15 @@
 
 	print_boot_stats();
 
-	iounmap(boot_stats);
-	iounmap(mpm_counter_base);
+	if (!(boot_marker_enabled()))
+		boot_stats_exit();
 
 	return 0;
 }
 
+int boot_stats_exit(void)
+{
+	iounmap(boot_stats);
+	iounmap(mpm_counter_base);
+	return 0;
+}
diff --git a/include/soc/qcom/boot_stats.h b/include/soc/qcom/boot_stats.h
index c81fc24..227d243 100644
--- a/include/soc/qcom/boot_stats.h
+++ b/include/soc/qcom/boot_stats.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014,2019 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
@@ -11,7 +11,40 @@
  */
 
 #ifdef CONFIG_MSM_BOOT_STATS
+
+#define TIMER_KHZ 32768
+extern struct boot_stats __iomem *boot_stats;
+
+struct boot_stats {
+	uint32_t bootloader_start;
+	uint32_t bootloader_end;
+	uint32_t bootloader_display;
+	uint32_t bootloader_load_kernel;
+	uint32_t load_kernel_start;
+	uint32_t load_kernel_end;
+#ifdef CONFIG_MSM_BOOT_TIME_MARKER
+	uint32_t bootloader_early_domain_start;
+	uint32_t bootloader_checksum;
+#endif
+};
+
 int boot_stats_init(void);
+int boot_stats_exit(void);
+unsigned long long int msm_timer_get_sclk_ticks(void);
+phys_addr_t msm_timer_get_pa(void);
 #else
 static inline int boot_stats_init(void) { return 0; }
+static inline unsigned long long int msm_timer_get_sclk_ticks(void)
+{
+	return 0;
+}
+static inline phys_addr_t msm_timer_get_pa(void) { return 0; }
+#endif
+
+#ifdef CONFIG_MSM_BOOT_TIME_MARKER
+static inline int boot_marker_enabled(void) { return 1; }
+void place_marker(const char *name);
+#else
+static inline void place_marker(char *name) { };
+static inline int boot_marker_enabled(void) { return 0; }
 #endif