Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b2a8ec3 authored by Venkata Rao Kakani's avatar Venkata Rao Kakani
Browse files

msm: boot markers: Add boot markers driver



Add boot marker driver and make support for boot stats.

Change-Id: Ie37cb7c5b44a391eb180499cf86ac50fcbbb544b
Signed-off-by: default avatarVenkata Rao Kakani <vkakani@codeaurora.org>
parent 1df57774
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -421,6 +421,16 @@ config MSM_BOOT_STATS
	  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
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ obj-$(CONFIG_QCOM_SCM) += scm.o
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_QCOM_DCC_V2) += dcc_v2.o
obj-$(CONFIG_MSM_GLADIATOR_HANG_DETECT) += gladiator_hang_detect.o
+238 −0
Original line number Diff line number Diff line
/* 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");
+59 −17
Original line number Diff line number Diff line
/* Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2017, 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)
{
@@ -88,6 +83,47 @@ static void print_boot_stats(void)
			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)
{
	int ret;
@@ -98,9 +134,15 @@ int boot_stats_init(void)

	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;
}
+34 −1
Original line number Diff line number Diff line
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2017, 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