| /* |
| * Copyright (c) 2018 Samsung Electronics Co., Ltd. |
| * http://www.samsung.com |
| * |
| * 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 |
| * published by the Free Software Foundation. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/slab.h> |
| #include <linux/syscalls.h> |
| #include <linux/ktime.h> |
| #include <linux/io.h> |
| #include <linux/sched/clock.h> |
| |
| #include <soc/samsung/exynos-bcm_dbg.h> |
| #include <soc/samsung/exynos-bcm_dbg-dump.h> |
| |
| static char file_name[128]; |
| |
| int exynos_bcm_dbg_buffer_dump(struct exynos_bcm_dbg_data *data, bool klog) |
| { |
| void __iomem *v_addr = data->dump_addr.v_addr; |
| u32 buff_size = data->dump_addr.buff_size - EXYNOS_BCM_KTIME_SIZE; |
| u32 buff_cnt = 0; |
| u32 dump_entry_size = sizeof(struct exynos_bcm_dump_info); |
| struct exynos_bcm_dump_info *dump_info = NULL; |
| u32 defined_event, ip_index; |
| char *result; |
| ssize_t str_size; |
| u32 tmp_ktime[2]; |
| u64 last_ktime; |
| struct file *fp = NULL; |
| mm_segment_t old_fs = get_fs(); |
| |
| if (!data->dump_addr.p_addr) { |
| BCM_ERR("%s: No memory region for dump\n", __func__); |
| return -ENOMEM; |
| } |
| |
| if (in_interrupt()) { |
| BCM_INFO("%s: skip file dump in interrupt context\n", __func__); |
| return 0; |
| } |
| |
| str_size = snprintf(file_name, PAGE_SIZE, "/data/result_bcm_%llu.csv", |
| cpu_clock(raw_smp_processor_id())); |
| |
| result = kzalloc(sizeof(char) * BCM_DUMP_MAX_STR, GFP_KERNEL); |
| if (result == NULL) { |
| BCM_ERR("%s: faild allocated of result memory\n", __func__); |
| return -ENOMEM; |
| } |
| |
| tmp_ktime[0] = __raw_readl(v_addr); |
| tmp_ktime[1] = __raw_readl(v_addr + 0x4); |
| last_ktime = (((u64)tmp_ktime[1] << EXYNOS_BCM_32BIT_SHIFT) & |
| EXYNOS_BCM_U64_HIGH_MASK) | |
| ((u64)tmp_ktime[0] & EXYNOS_BCM_U64_LOW_MASK); |
| |
| dump_info = (struct exynos_bcm_dump_info *)(v_addr + EXYNOS_BCM_KTIME_SIZE); |
| |
| set_fs(KERNEL_DS); |
| |
| fp = filp_open(file_name, O_WRONLY|O_CREAT|O_APPEND, 0); |
| if (IS_ERR(fp)) { |
| BCM_ERR("%s: name: %s filp_open fail\n", __func__, file_name); |
| set_fs(old_fs); |
| kfree(result); |
| return IS_ERR(fp); |
| } |
| |
| str_size = snprintf(result, PAGE_SIZE, "last kernel time, %llu\n", last_ktime); |
| vfs_write(fp, result, str_size, &fp->f_pos); |
| |
| if (data->bcm_cnt_nr == 4) { |
| str_size = snprintf(result, PAGE_SIZE, "seq_no, ip_index, define_event, time, \ |
| ccnt, pmcnt0, pmcnt1, pmcnt2, pmcnt3\n"); |
| } else if (data->bcm_cnt_nr == 8) { |
| str_size = snprintf(result, PAGE_SIZE, "seq_no, ip_index, define_event, time, \ |
| ccnt, pmcnt0, pmcnt1, pmcnt2, pmcnt3, " |
| "pmcnt4, pmcnt5, pmcnt6, pmcnt7\n"); |
| } |
| vfs_write(fp, result, str_size, &fp->f_pos); |
| |
| if (klog) |
| pr_info("%s", result); |
| |
| while ((buff_size - buff_cnt) > dump_entry_size) { |
| defined_event = BCM_CMD_GET(dump_info->dump_header, |
| BCM_EVT_PRE_DEFINE_MASK, BCM_DUMP_PRE_DEFINE_SHIFT); |
| ip_index = BCM_CMD_GET(dump_info->dump_header, BCM_IP_MASK, 0); |
| |
| if (data->bcm_cnt_nr == 4) { |
| str_size = snprintf(result, PAGE_SIZE, "%u, %u, %u, %u, %u, %u, %u, %u, %u\n", |
| dump_info->dump_seq_no, ip_index, defined_event, |
| dump_info->dump_time, dump_info->out_data.ccnt, |
| dump_info->out_data.pmcnt[0], dump_info->out_data.pmcnt[1], |
| dump_info->out_data.pmcnt[2], dump_info->out_data.pmcnt[3]); |
| } else if (data->bcm_cnt_nr == 8) { |
| str_size = snprintf(result, PAGE_SIZE, "%u, %u, %u, %u, %u, %u, \ |
| %u, %u, %u, %u, %u, %u, %u\n", |
| dump_info->dump_seq_no, ip_index, defined_event, |
| dump_info->dump_time, dump_info->out_data.ccnt, |
| dump_info->out_data.pmcnt[0], dump_info->out_data.pmcnt[1], |
| dump_info->out_data.pmcnt[2], dump_info->out_data.pmcnt[3], |
| dump_info->out_data.pmcnt[4], dump_info->out_data.pmcnt[5], |
| dump_info->out_data.pmcnt[6], dump_info->out_data.pmcnt[7]); |
| } |
| vfs_write(fp, result, str_size, &fp->f_pos); |
| |
| if (klog) |
| pr_info("%s", result); |
| |
| dump_info++; |
| buff_cnt += dump_entry_size; |
| } |
| |
| filp_close(fp, NULL); |
| set_fs(old_fs); |
| kfree(result); |
| |
| return 0; |
| } |