Loading drivers/soc/qcom/Kconfig +10 −0 Original line number Diff line number Diff line Loading @@ -184,6 +184,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_CPUSS_DUMP bool "CPU Subsystem Dumping support" help Loading drivers/soc/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ obj-$(CONFIG_MSM_GLADIATOR_ERP) += gladiator_erp.o obj-$(CONFIG_MSM_GLADIATOR_ERP_V2) += gladiator_erp_v2.o obj-$(CONFIG_ARM64) += idle-v8.o cpu_ops.o obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o obj-$(CONFIG_MSM_BOOT_TIME_MARKER) += boot_marker.o obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o obj-$(CONFIG_CPU_V7) += idle-v7.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o Loading drivers/soc/qcom/boot_marker.c 0 → 100644 +174 −0 Original line number Diff line number Diff line /* Copyright (c) 2016, 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 <soc/qcom/boot_stats.h> #define MAX_STRING_LEN 100 #define BOOT_MARKER_MAX_LEN 20 static struct dentry *dent_bkpi, *dent_bkpi_status; static struct boot_marker boot_marker_list; struct boot_marker { char marker_name[BOOT_MARKER_MAX_LEN]; unsigned long long int timer_value; struct list_head list; struct mutex lock; }; static void _create_boot_marker(const char *name, unsigned long long int timer_value) { struct boot_marker *new_boot_marker; pr_debug("%-22s:%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("Lk Start - ", readl_relaxed(&boot_stats->bootloader_start)); _create_boot_marker("Lk End - ", readl_relaxed(&boot_stats->bootloader_end)); _create_boot_marker("Lk Display - ", readl_relaxed(&boot_stats->bootloader_display)); _create_boot_marker("Lk Load Kernel - ", readl_relaxed(&boot_stats->bootloader_load_kernel)); } 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, "%-22s:%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) { char buf[MAX_STRING_LEN]; if (count > MAX_STRING_LEN) return -EINVAL; place_marker((char *) user_buffer); return simple_write_to_buffer(buf, MAX_STRING_LEN, position, user_buffer, count); } 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 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("kpi_values", (S_IRUGO|S_IWUGO), dent_bkpi, 0, &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; } INIT_LIST_HEAD(&boot_marker_list.list); mutex_init(&boot_marker_list.lock); set_bootloader_stats(); return 0; } module_init(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"); drivers/soc/qcom/boot_stats.c +49 −11 Original line number Diff line number Diff line /* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014,2016, 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 Loading @@ -15,6 +15,7 @@ #include <linux/io.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/clk.h> Loading @@ -22,17 +23,13 @@ #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 <linux/export.h> #include <linux/types.h> #include <soc/qcom/boot_stats.h> static void __iomem *mpm_counter_base; 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) { Loading Loading @@ -88,6 +85,42 @@ 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; int 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; } int boot_stats_init(void) { int ret; Loading @@ -98,9 +131,14 @@ int boot_stats_init(void) print_boot_stats(); if (!(boot_marker_enabled())) boot_stats_exit(); return 0; } int boot_stats_exit(void) { iounmap(boot_stats); iounmap(mpm_counter_base); return 0; } include/soc/qcom/boot_stats.h +24 −1 Original line number Diff line number Diff line /* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014,2016 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 Loading @@ -11,7 +11,30 @@ */ #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; }; int boot_stats_init(void); int boot_stats_exit(void); unsigned long long int msm_timer_get_sclk_ticks(void); #else static inline int boot_stats_init(void) { return 0; } unsigned long long int msm_timer_get_sclk_ticks(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 inline void place_marker(char *name); static inline int boot_marker_enabled(void) { return 0; } #endif Loading
drivers/soc/qcom/Kconfig +10 −0 Original line number Diff line number Diff line Loading @@ -184,6 +184,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_CPUSS_DUMP bool "CPU Subsystem Dumping support" help Loading
drivers/soc/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ obj-$(CONFIG_MSM_GLADIATOR_ERP) += gladiator_erp.o obj-$(CONFIG_MSM_GLADIATOR_ERP_V2) += gladiator_erp_v2.o obj-$(CONFIG_ARM64) += idle-v8.o cpu_ops.o obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o obj-$(CONFIG_MSM_BOOT_TIME_MARKER) += boot_marker.o obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o obj-$(CONFIG_CPU_V7) += idle-v7.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o Loading
drivers/soc/qcom/boot_marker.c 0 → 100644 +174 −0 Original line number Diff line number Diff line /* Copyright (c) 2016, 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 <soc/qcom/boot_stats.h> #define MAX_STRING_LEN 100 #define BOOT_MARKER_MAX_LEN 20 static struct dentry *dent_bkpi, *dent_bkpi_status; static struct boot_marker boot_marker_list; struct boot_marker { char marker_name[BOOT_MARKER_MAX_LEN]; unsigned long long int timer_value; struct list_head list; struct mutex lock; }; static void _create_boot_marker(const char *name, unsigned long long int timer_value) { struct boot_marker *new_boot_marker; pr_debug("%-22s:%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("Lk Start - ", readl_relaxed(&boot_stats->bootloader_start)); _create_boot_marker("Lk End - ", readl_relaxed(&boot_stats->bootloader_end)); _create_boot_marker("Lk Display - ", readl_relaxed(&boot_stats->bootloader_display)); _create_boot_marker("Lk Load Kernel - ", readl_relaxed(&boot_stats->bootloader_load_kernel)); } 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, "%-22s:%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) { char buf[MAX_STRING_LEN]; if (count > MAX_STRING_LEN) return -EINVAL; place_marker((char *) user_buffer); return simple_write_to_buffer(buf, MAX_STRING_LEN, position, user_buffer, count); } 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 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("kpi_values", (S_IRUGO|S_IWUGO), dent_bkpi, 0, &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; } INIT_LIST_HEAD(&boot_marker_list.list); mutex_init(&boot_marker_list.lock); set_bootloader_stats(); return 0; } module_init(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");
drivers/soc/qcom/boot_stats.c +49 −11 Original line number Diff line number Diff line /* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014,2016, 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 Loading @@ -15,6 +15,7 @@ #include <linux/io.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/clk.h> Loading @@ -22,17 +23,13 @@ #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 <linux/export.h> #include <linux/types.h> #include <soc/qcom/boot_stats.h> static void __iomem *mpm_counter_base; 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) { Loading Loading @@ -88,6 +85,42 @@ 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; int 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; } int boot_stats_init(void) { int ret; Loading @@ -98,9 +131,14 @@ int boot_stats_init(void) print_boot_stats(); if (!(boot_marker_enabled())) boot_stats_exit(); return 0; } int boot_stats_exit(void) { iounmap(boot_stats); iounmap(mpm_counter_base); return 0; }
include/soc/qcom/boot_stats.h +24 −1 Original line number Diff line number Diff line /* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014,2016 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 Loading @@ -11,7 +11,30 @@ */ #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; }; int boot_stats_init(void); int boot_stats_exit(void); unsigned long long int msm_timer_get_sclk_ticks(void); #else static inline int boot_stats_init(void) { return 0; } unsigned long long int msm_timer_get_sclk_ticks(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 inline void place_marker(char *name); static inline int boot_marker_enabled(void) { return 0; } #endif