898 lines
24 KiB
C
Executable file
898 lines
24 KiB
C
Executable file
/*
|
|
* Exynos CPU Performance
|
|
* memcpy/memset: cacheable/non-cacheable
|
|
* Author: Jungwook Kim, jwook1.kim@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/of.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/module.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/time.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/cpufreq.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <linux/swiotlb.h>
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/kobject.h>
|
|
#include <linux/string.h>
|
|
#include <linux/sysfs.h>
|
|
#include <linux/mod_devicetable.h>
|
|
#include <linux/init.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <linux/io.h>
|
|
#include <linux/syscalls.h>
|
|
#include <asm/io.h>
|
|
#include <asm/neon.h>
|
|
|
|
#define _PMU_COUNT_64
|
|
#include "exynos_perf_pmufunc.h"
|
|
#include <soc/samsung/cal-if.h>
|
|
#include <soc/samsung/exynos-devfreq.h>
|
|
|
|
#define EVENT_MAX 7
|
|
|
|
struct device *memcpy_dev;
|
|
static uint cal_id_mif = 0;
|
|
static uint devfreq_mif = 0;
|
|
|
|
enum memtype {
|
|
MT_CACHE = 1,
|
|
MT_NC,
|
|
MT_C2NC,
|
|
MT_NC2C,
|
|
MT_ION,
|
|
MT_ION_INF,
|
|
MT_COUNT,
|
|
};
|
|
|
|
enum functype {
|
|
FT_MEMCPY = 1,
|
|
FT_MEMSET,
|
|
FT_CPYTOUSR,
|
|
FT_CPYFRMUSR,
|
|
FT_CACHE_FLUSH,
|
|
FT_COUNT,
|
|
};
|
|
|
|
static char *memtype_str[] = {"", "MT_CACHE", "MT_NC", "MT_C2NC", "MT_NC2C", "MT_ION", "MT_ION_INF"};
|
|
static char *functype_str[] = {"",
|
|
"memcpy",
|
|
"memset",
|
|
"__copy_to_user",
|
|
"__copy_from_user",
|
|
"cache_flush",
|
|
};
|
|
//static char *level_str[] = { "ALL_CL", "LOUIS", "LLC" };
|
|
static uint type = MT_CACHE;
|
|
static uint start_size = 64;
|
|
static uint end_size = SZ_4M;
|
|
static uint core = 4;
|
|
static uint run = 0;
|
|
static uint func = 1;
|
|
static int val = 0;
|
|
static uint align = 0;
|
|
static uint flush = 0;
|
|
static uint dump = 0;
|
|
static uint pmu = 0;
|
|
static uint chk = 0;
|
|
static uint src_mset = 0;
|
|
static char nc_memcpy_result_buf[PAGE_SIZE];
|
|
static const char *prefix = "exynos_perf_ncmemcpy";
|
|
static int nc_memcpy_done = 0;
|
|
|
|
// cache flush settings
|
|
static uint level = 2;
|
|
static uint setway = 1;
|
|
|
|
static int events[7] = {0x50,0x51,0x52,0x53,0x60,0x61,0};
|
|
|
|
#define GiB (1024*1024*1024LL)
|
|
#define GB 1000000000LL
|
|
#define MiB (1024*1024)
|
|
#define NS_PER_SEC 1000000000LL
|
|
#define US_PER_SEC 1000000LL
|
|
|
|
static u64 patterns[] = {
|
|
/* The first entry has to be 0 to leave memtest with zeroed memory */
|
|
0,
|
|
0xffffffffffffffffULL,
|
|
0x5555555555555555ULL,
|
|
0xaaaaaaaaaaaaaaaaULL,
|
|
0x1111111111111111ULL,
|
|
0x2222222222222222ULL,
|
|
0x4444444444444444ULL,
|
|
0x8888888888888888ULL,
|
|
0x3333333333333333ULL,
|
|
0x6666666666666666ULL,
|
|
0x9999999999999999ULL,
|
|
0xccccccccccccccccULL,
|
|
0x7777777777777777ULL,
|
|
0xbbbbbbbbbbbbbbbbULL,
|
|
0xddddddddddddddddULL,
|
|
0xeeeeeeeeeeeeeeeeULL,
|
|
0x7a6c7258554e494cULL, /* yeah ;-) */
|
|
};
|
|
|
|
static u64 nano_time(void)
|
|
{
|
|
struct timespec t;
|
|
t.tv_sec = t.tv_nsec = 0;
|
|
getnstimeofday(&t);
|
|
return (t.tv_sec * NS_PER_SEC + t.tv_nsec);
|
|
}
|
|
|
|
static void memset_ptrn(ulong *p, uint count)
|
|
{
|
|
uint j, idx;
|
|
for (j = 0; j < count; j += sizeof(p)) {
|
|
idx = j % ARRAY_SIZE(patterns);
|
|
*p++ = patterns[idx];
|
|
}
|
|
}
|
|
|
|
#ifdef __aarch64__
|
|
#define MEMSET_FUNC(FUNC) \
|
|
total_size = (core < 4)? 128*MiB : GiB; \
|
|
iter = total_size / xfer_size; \
|
|
for (i = 0; i < iter; i++) { \
|
|
start_ns = nano_time(); \
|
|
FUNC; \
|
|
end_ns = nano_time(); \
|
|
sum_ns += (end_ns - start_ns); \
|
|
} \
|
|
bw = total_size / MiB * NS_PER_SEC / sum_ns; \
|
|
dst_align = (ulong)dst + align; \
|
|
dst_align &= 0xfffff; \
|
|
ret1 += snprintf(buf + ret1, PAGE_SIZE - ret1, "%-20s %5d %8s %7d %7d " \
|
|
"%10u %10llu %10u %15llu %15lx\n", \
|
|
functype_str[func], core, memtype_str[type], cpufreq, mif, \
|
|
xfer_size, bw, iter, sum_ns, dst_align);
|
|
|
|
#if defined(CONFIG_SOC_EXYNOS9810) || defined(CONFIG_SOC_EXYNOS9610)
|
|
#define MEMCPY_FUNC(FUNC, do_flush, is_ret, do_pmu, do_chk, is_neon) \
|
|
total_size = (do_flush || core < 4 || type == MT_NC)? 128*MiB : GiB; \
|
|
iter = total_size / xfer_size; \
|
|
for (k = 0; k < iter; k++) { \
|
|
if (src_mset) \
|
|
memset_ptrn((ulong *)src + align, xfer_size); \
|
|
if (do_pmu) { \
|
|
__start_timer(); \
|
|
for (i = 0; i < EVENT_MAX; i++) { \
|
|
pmu_enable_counter(i); \
|
|
pmnc_config(i, events[i]); \
|
|
} \
|
|
pmnc_reset(); \
|
|
} \
|
|
if (is_neon) \
|
|
kernel_neon_begin(); \
|
|
start_ns = nano_time(); \
|
|
FUNC; \
|
|
end_ns = nano_time(); \
|
|
if (is_neon) \
|
|
kernel_neon_end(); \
|
|
sum_ns += (end_ns - start_ns); \
|
|
if (do_pmu) { \
|
|
ccnt += __stop_timer(); \
|
|
for(i = 0; i < EVENT_MAX; i++) \
|
|
v[i] += pmnc_read2(i); \
|
|
} \
|
|
if (do_chk) \
|
|
chk_sum += (memcmp(dst, src + align, xfer_size) != 0)? 1 : 0;\
|
|
} \
|
|
if (is_ret && ret > 0) { \
|
|
pr_info("bench failed: ret > 0\n"); \
|
|
continue; \
|
|
} \
|
|
bw = total_size / MiB * NS_PER_SEC / sum_ns; \
|
|
src_align = (ulong)src + align; \
|
|
src_align &= 0xfffff; \
|
|
if (do_pmu) { \
|
|
ret1 += snprintf(buf + ret1, PAGE_SIZE - ret1, "%-20s %5d %8s %7d %7d " \
|
|
"%10u %10llu %10u %15llu %15lu " \
|
|
"%15lu %15lu %15lu %15lu %15lu %15lu %15lu " \
|
|
"%15lx %7d %5lu\n", \
|
|
functype_str[func], core, memtype_str[type], cpufreq, mif, \
|
|
xfer_size, bw, iter, sum_ns, ccnt, \
|
|
v[0], v[1], v[2], v[3], v[4], v[5], v[6], \
|
|
src_align, do_flush, chk_sum); \
|
|
} else { \
|
|
ret1 += snprintf(buf + ret1, PAGE_SIZE - ret1, "%-20s %5d %8s %7d %7d " \
|
|
"%10u %10llu %15u %15llu %15lx %7d %5lu\n", \
|
|
functype_str[func], core, memtype_str[type], cpufreq, mif, \
|
|
xfer_size, bw, iter, sum_ns, src_align, do_flush, chk_sum);\
|
|
}
|
|
#else
|
|
#define MEMCPY_FUNC(FUNC, do_flush, is_ret, do_pmu, do_chk, is_neon) \
|
|
total_size = (do_flush || core < 4 || type == MT_NC)? 128*MiB : GiB; \
|
|
iter = total_size / xfer_size; \
|
|
for (k = 0; k < iter; k++) { \
|
|
if (src_mset) \
|
|
memset_ptrn((ulong *)src + align, xfer_size); \
|
|
if (do_pmu) { \
|
|
__start_timer(); \
|
|
for (i = 0; i < EVENT_MAX; i++) { \
|
|
pmu_enable_counter(i); \
|
|
pmnc_config(i, events[i]); \
|
|
} \
|
|
pmnc_reset(); \
|
|
} \
|
|
if (is_neon) \
|
|
kernel_neon_begin(); \
|
|
start_ns = nano_time(); \
|
|
FUNC; \
|
|
end_ns = nano_time(); \
|
|
if (is_neon) \
|
|
kernel_neon_end(); \
|
|
sum_ns += (end_ns - start_ns); \
|
|
if (do_pmu) { \
|
|
ccnt += __stop_timer(); \
|
|
for(i = 0; i < EVENT_MAX; i++) \
|
|
v[i] += pmnc_read2(i); \
|
|
} \
|
|
if (do_chk) \
|
|
chk_sum += (memcmp(dst, src + align, xfer_size) != 0)? 1 : 0;\
|
|
} \
|
|
if (is_ret && ret > 0) { \
|
|
pr_info("[%s] bench failed: ret > 0\n", prefix); \
|
|
continue; \
|
|
} \
|
|
bw = total_size / MiB * NS_PER_SEC / sum_ns; \
|
|
src_align = (ulong)src + align; \
|
|
src_align &= 0xfffff; \
|
|
if (do_pmu) { \
|
|
ret1 += snprintf(buf + ret1, PAGE_SIZE - ret1, "%-20s %5d %8s %7d %7d " \
|
|
"%10u %10llu %10u %15llu %15lu " \
|
|
"%15lu %15lu %15lu %15lu %15lu %15lu %15lu " \
|
|
"%15lx %7d %5lu\n", \
|
|
functype_str[func], core, memtype_str[type], cpufreq, mif, \
|
|
xfer_size, bw, iter, sum_ns, ccnt, \
|
|
v[0], v[1], v[2], v[3], v[4], v[5], v[6], \
|
|
src_align, do_flush, chk_sum); \
|
|
} else { \
|
|
ret1 += snprintf(buf + ret1, PAGE_SIZE - ret1, "%-20s %5d %8s %7d %7d " \
|
|
"%10u %10llu %15u %15llu %15lx %7d %5lu\n", \
|
|
functype_str[func], core, memtype_str[type], cpufreq, mif, \
|
|
xfer_size, bw, iter, sum_ns, src_align, do_flush, chk_sum);\
|
|
}
|
|
#endif
|
|
|
|
#define CACHE_FLUSH_TEST(FUNC, do_flush, do_chk) \
|
|
iter = 100; \
|
|
for (i = 0; i < iter; i++) { \
|
|
memset_ptrn((ulong *)src + align, xfer_size); \
|
|
memset(dst, 0x0, xfer_size); \
|
|
memcpy(dst, src + align, xfer_size); \
|
|
if (do_flush) { \
|
|
start_ns = nano_time(); \
|
|
FUNC; \
|
|
end_ns = nano_time(); \
|
|
sum_ns += (end_ns - start_ns); \
|
|
} \
|
|
/* if (do_chk) \
|
|
chk_sum += (memcmp(dst_remap, dst, xfer_size) != 0)? 1 : 0; */\
|
|
} \
|
|
avg_us = sum_ns / 1000 / iter; \
|
|
ret1 += snprintf(buf + ret1, PAGE_SIZE - ret1, "%-20s %5d %8s %7d %7d " \
|
|
"%10u %10llu %7u %10llu %5d %5lu %8d %10s\n", \
|
|
functype_str[func], core, memtype_str[type], cpufreq, mif, \
|
|
xfer_size, avg_us, iter, sum_ns, do_flush, chk_sum, setway, level_str[level]);
|
|
|
|
#endif
|
|
|
|
void func_perf(void *src, void *dst)
|
|
{
|
|
u64 start_ns, end_ns, bw, sum_ns; //avg_us;
|
|
ulong chk_sum;
|
|
uint total_size;
|
|
uint iter;
|
|
int i, k;
|
|
uint xfer_size = start_size;
|
|
size_t ret;
|
|
ulong src_align;
|
|
ulong dst_align;
|
|
ccnt_t v[7] = {0,};
|
|
ccnt_t ccnt;
|
|
|
|
static char buf[PAGE_SIZE];
|
|
uint cpufreq;
|
|
uint mif = 0;
|
|
bool is_need_warm_up = true;
|
|
size_t ret1 = 0;
|
|
|
|
nc_memcpy_done = 0;
|
|
|
|
// HEADER
|
|
if (pmu) {
|
|
ret1 += snprintf(buf + ret1, PAGE_SIZE - ret1, "%-20s %5s %8s %7s %7s %10s %10s %15s %10s %15s "
|
|
"%8s(0x%x) %8s(0x%x) %8s(0x%x) %8s(0x%x) %8s(0x%x) %8s(0x%x) %8s(0x%x) %15s %7s %5s\n",
|
|
"name", "core", "type", "cpufreq", "mif", "size", "MiB/s", "iter", "total_ns", "total_ccnt",
|
|
"e0", events[0],
|
|
"e1", events[1],
|
|
"e2", events[2],
|
|
"e3", events[3],
|
|
"e4", events[4],
|
|
"e5", events[5],
|
|
"e6", events[6],
|
|
"src+align", "flush", "chk");
|
|
} else if (func == FT_MEMSET) {
|
|
ret1 += snprintf(buf + ret1, PAGE_SIZE - ret1, "%-20s %5s %8s %7s %7s %10s %10s %15s %10s %15s\n",
|
|
"name", "core", "type", "cpufreq", "mif", "size", "MiB/s", "iter", "total_ns", "dst+align");
|
|
} else if (func == FT_CACHE_FLUSH) {
|
|
ret1 += snprintf(buf + ret1, PAGE_SIZE - ret1, "%-20s %5s %8s %7s %7s %10s %10s %7s %10s %5s %5s %8s %10s\n",
|
|
"name", "core", "type", "cpufreq", "mif", "size", "avg_us", "iter", "total_ns", "flush", "chk", "setway", "level");
|
|
} else {
|
|
ret1 += snprintf(buf + ret1, PAGE_SIZE - ret1, "%-20s %5s %8s %7s %7s %10s %10s %15s %15s %15s %7s %5s\n",
|
|
"name", "core", "type", "cpufreq", "mif", "size", "MiB/s", "iter", "total_ns", "src+align", "flush", "chk");
|
|
}
|
|
|
|
// BODY - START
|
|
bw = 0;
|
|
while (xfer_size <= end_size) {
|
|
ret = 0;
|
|
sum_ns = 0;
|
|
ccnt = 0;
|
|
chk_sum = 0;
|
|
v[0] = v[1] = v[2] = v[3] = v[4] = v[5] = v[6] = 0;
|
|
cpufreq = cpufreq_quick_get(core) / 1000;
|
|
#if defined(CONFIG_SOC_EXYNOS9830) || defined(CONFIG_SOC_EXYNOS991)
|
|
mif = (uint)exynos_devfreq_get_domain_freq(devfreq_mif) / 1000;
|
|
#else
|
|
mif = (uint)cal_dfs_cached_get_rate(cal_id_mif) / 1000;
|
|
#endif
|
|
switch (func) {
|
|
case FT_MEMCPY:
|
|
MEMCPY_FUNC(memcpy(dst, src + align, xfer_size), flush, 0, pmu, chk, 0);
|
|
break;
|
|
case FT_MEMSET:
|
|
MEMSET_FUNC(memset(dst, val, xfer_size));
|
|
break;
|
|
case FT_CPYTOUSR:
|
|
MEMCPY_FUNC(ret += __copy_to_user(dst, src + align, xfer_size), flush, 1, pmu, chk, 0);
|
|
break;
|
|
case FT_CPYFRMUSR:
|
|
MEMCPY_FUNC(ret += __copy_from_user(dst, src + align, xfer_size), flush, 1, pmu, chk, 0);
|
|
break;
|
|
case FT_CACHE_FLUSH:
|
|
#if 0
|
|
if (setway == 0) {
|
|
CACHE_FLUSH_TEST(__dma_flush_area(src + align, xfer_size), flush, chk);
|
|
} else {
|
|
switch (level) {
|
|
case 1: CACHE_FLUSH_TEST(flush_cache_louis(), flush, chk); break;
|
|
case 2: CACHE_FLUSH_TEST(flush_cache_all(), flush, chk); break;
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
default:
|
|
pr_info("[%s] func type = %d invalid!\n", prefix, func);
|
|
break;
|
|
}
|
|
|
|
if (is_need_warm_up) { // run first iteration 2 times for warm up
|
|
is_need_warm_up = false;
|
|
} else {
|
|
xfer_size *= 2;
|
|
}
|
|
}
|
|
// BODY - END
|
|
|
|
memcpy(nc_memcpy_result_buf, buf, PAGE_SIZE);
|
|
|
|
nc_memcpy_done = 1;
|
|
}
|
|
|
|
static int perf_main(void)
|
|
{
|
|
struct device *dev = memcpy_dev;
|
|
void *src_cpu, *dst_cpu;
|
|
dma_addr_t src_dma, dst_dma;
|
|
gfp_t gfp;
|
|
uint buf_size;
|
|
u64 start_ns, end_ns;
|
|
start_ns = nano_time();
|
|
|
|
/* dma address */
|
|
if (!dma_set_mask(dev, DMA_BIT_MASK(64))) {
|
|
pr_info("[%s] dma_mask: 64-bits ok\n", prefix);
|
|
} else if (!dma_set_mask(dev, DMA_BIT_MASK(32))) {
|
|
pr_info("[%s] dma_mask: 32-bits ok\n", prefix);
|
|
}
|
|
|
|
buf_size = (align == 0)? end_size : end_size + 64;
|
|
gfp = GFP_KERNEL;
|
|
|
|
if (type == MT_CACHE) {
|
|
gfp = GFP_KERNEL;
|
|
src_cpu = kmalloc(buf_size, gfp);
|
|
if (src_cpu == NULL) {
|
|
pr_info("[%s] kmalloc failed: src\n", prefix);
|
|
return 0;
|
|
}
|
|
dst_cpu = kmalloc(buf_size, gfp);
|
|
if (dst_cpu == NULL) {
|
|
pr_info("[%s] kmalloc failed: dst\n", prefix);
|
|
kfree(src_cpu);
|
|
return 0;
|
|
}
|
|
func_perf(src_cpu, dst_cpu);
|
|
kfree(dst_cpu);
|
|
kfree(src_cpu);
|
|
|
|
} else if (type == MT_NC) {
|
|
#if 1
|
|
src_cpu = dma_alloc_coherent(dev, buf_size, &src_dma, gfp);
|
|
if (src_cpu == NULL) {
|
|
pr_info("[%s] dma_alloc_coherent failed: src: buf_size=%d\n", prefix, buf_size);
|
|
return 0;
|
|
}
|
|
dst_cpu = dma_alloc_coherent(dev, buf_size, &dst_dma, gfp);
|
|
if (dst_cpu == NULL) {
|
|
pr_info("[%s] dma_alloc_coherent failed: dst: buf_size=%d\n", prefix, buf_size);
|
|
dma_free_coherent(dev, buf_size, src_cpu, src_dma);
|
|
return 0;
|
|
}
|
|
func_perf(src_cpu, dst_cpu);
|
|
dma_free_coherent(dev, buf_size, dst_cpu, dst_dma);
|
|
dma_free_coherent(dev, buf_size, src_cpu, src_dma);
|
|
#else
|
|
src_cpu = swiotlb_alloc_coherent(dev, size, &src_dma, gfp);
|
|
if (src_cpu == NULL) {
|
|
pr_info("dma_alloc_coherent failed: src\n");
|
|
return 0;
|
|
}
|
|
dst_cpu = swiotlb_alloc_coherent(dev, size, &dst_dma, gfp);
|
|
if (dst_cpu == NULL) {
|
|
pr_info("dma_alloc_coherent failed: dst\n");
|
|
dma_free_coherent(dev, size, src_cpu, src_dma);
|
|
return 0;
|
|
}
|
|
func_perf(src_cpu, dst_cpu);
|
|
dma_free_coherent(dev, size, dst_cpu, dst_dma);
|
|
dma_free_coherent(dev, size, src_cpu, src_dma);
|
|
|
|
/*
|
|
int order = get_order(size);
|
|
src_cpu = (void *)__get_free_pages(gfp, order);
|
|
if (src_cpu == NULL) {
|
|
pr_info("__get_free_pages failed: src\n");
|
|
return 0;
|
|
}
|
|
src_dma = (dma_addr_t)virt_to_phys(src_cpu);
|
|
dst_cpu = (void *)__get_free_pages(gfp, order);
|
|
if (dst_cpu == NULL) {
|
|
pr_info("__get_free_pages failed: dst\n");
|
|
return 0;
|
|
}
|
|
dst_dma = (dma_addr_t)virt_to_phys(dst_cpu);
|
|
func_perf(src_cpu, dst_cpu);
|
|
free_pages((ulong)dst_cpu, order);
|
|
free_pages((ulong)src_cpu, order);
|
|
*/
|
|
#endif
|
|
} else if (type == MT_C2NC) {
|
|
/* cacheable --> non-cacheable */
|
|
pr_info("[%s] cacheable --> non-cacheable\n", prefix);
|
|
src_cpu = kmalloc(buf_size, gfp);
|
|
if (src_cpu == NULL) {
|
|
pr_info("[%s] kmalloc failed: src\n", prefix);
|
|
return 0;
|
|
}
|
|
dst_cpu = dma_alloc_coherent(dev, buf_size, &dst_dma, gfp);
|
|
if (dst_cpu == NULL) {
|
|
pr_info("[%s] dma_alloc_coherent failed: dst\n", prefix);
|
|
kfree(src_cpu);
|
|
return 0;
|
|
}
|
|
func_perf(src_cpu, dst_cpu);
|
|
dma_free_coherent(dev, buf_size, dst_cpu, dst_dma);
|
|
kfree(src_cpu);
|
|
} else if (type == MT_NC2C) {
|
|
/* non-cacheable --> cacheable */
|
|
pr_info("[%s] non-cacheable --> cacheable\n", prefix);
|
|
src_cpu = dma_alloc_coherent(dev, buf_size, &src_dma, gfp);
|
|
if (src_cpu == NULL) {
|
|
pr_info("[%s] dma_alloc_coherent failed: src\n", prefix);
|
|
return 0;
|
|
}
|
|
dst_cpu = kmalloc(buf_size, gfp);
|
|
if (dst_cpu == NULL) {
|
|
pr_info("[%s] kmalloc failed: dst\n", prefix);
|
|
dma_free_coherent(dev, buf_size, src_cpu, src_dma);
|
|
return 0;
|
|
}
|
|
func_perf(src_cpu, dst_cpu);
|
|
dma_free_coherent(dev, buf_size, src_cpu, src_dma);
|
|
kfree(dst_cpu);
|
|
#if 0
|
|
} else if (type == MT_ION) {
|
|
// alloc_ctx = vb2_ion_create_context(dev, SZ_4K, VB2ION_CTX_IOMMU | VB2ION_CTX_VMCONTIG);
|
|
alloc_ctx = vb2_ion_create_context(dev, SZ_4K, VB2ION_CTX_VMCONTIG);
|
|
if (IS_ERR(alloc_ctx)) {
|
|
pr_info("-----> ion context failed = %ld\n", PTR_ERR(alloc_ctx));
|
|
return 0;
|
|
}
|
|
cookie = vb2_ion_private_alloc(alloc_ctx, buf_size * 2);
|
|
if (IS_ERR(cookie)) {
|
|
pr_info("-----> ion alloc failed = %ld\n", PTR_ERR(cookie));
|
|
return 0;
|
|
}
|
|
src_cpu = vb2_ion_private_vaddr(cookie);
|
|
if (src_cpu == NULL) {
|
|
pr_info("-----> ion vaddr failed\n");
|
|
return 0;
|
|
}
|
|
dst_cpu = src_cpu + buf_size;
|
|
vb2_ion_sync_for_device(cookie, 0, buf_size * 2, DMA_BIDIRECTIONAL);
|
|
|
|
func_perf(src_cpu, dst_cpu);
|
|
func_perf(dst_cpu, src_cpu);
|
|
|
|
vb2_ion_private_free(cookie);
|
|
vb2_ion_destroy_context(alloc_ctx);
|
|
} else if (type == MT_ION_INF) {
|
|
alloc_ctx = vb2_ion_create_context(dev, SZ_4K, VB2ION_CTX_VMCONTIG);
|
|
if (IS_ERR(alloc_ctx)) {
|
|
pr_info("-----> ion context failed = %ld\n", PTR_ERR(alloc_ctx));
|
|
return 0;
|
|
}
|
|
cookie = vb2_ion_private_alloc(alloc_ctx, buf_size * 2);
|
|
if (IS_ERR(cookie)) {
|
|
pr_info("-----> ion alloc failed = %ld\n", PTR_ERR(cookie));
|
|
return 0;
|
|
}
|
|
src_cpu = vb2_ion_private_vaddr(cookie);
|
|
if (src_cpu == NULL) {
|
|
pr_info("-----> ion vaddr failed\n");
|
|
return 0;
|
|
}
|
|
dst_cpu = src_cpu + buf_size;
|
|
vb2_ion_sync_for_device(cookie, 0, buf_size * 2, DMA_BIDIRECTIONAL);
|
|
|
|
while (1) {
|
|
func_perf(src_cpu, dst_cpu);
|
|
func_perf(dst_cpu, src_cpu);
|
|
}
|
|
|
|
vb2_ion_private_free(cookie);
|
|
vb2_ion_destroy_context(alloc_ctx);
|
|
#endif
|
|
} else {
|
|
pr_info("[%s] type = %d invalid!\n", prefix, type);
|
|
}
|
|
|
|
end_ns = nano_time();
|
|
pr_info("[%s] Total elapsed time = %llu seconds\n", prefix, (end_ns - start_ns) / NS_PER_SEC);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* thread main function */
|
|
static bool thread_running;
|
|
static int thread_func(void *data)
|
|
{
|
|
thread_running = 1;
|
|
perf_main();
|
|
thread_running = 0;
|
|
return 0;
|
|
}
|
|
|
|
static struct task_struct *task;
|
|
static int test_thread_init(void)
|
|
{
|
|
task = kthread_create(thread_func, NULL, "thread%u", 0);
|
|
kthread_bind(task, core);
|
|
wake_up_process(task);
|
|
return 0;
|
|
}
|
|
|
|
static void test_thread_exit(void)
|
|
{
|
|
printk(KERN_ERR "[%s] Exit module: thread_running: %d\n", prefix, thread_running);
|
|
if (thread_running)
|
|
kthread_stop(task);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* sysfs starts here
|
|
*-------------------------------------------------------------------------------*/
|
|
// macro for normal nodes
|
|
#define DEF_NODE(name) \
|
|
static int name##_seq_show(struct seq_file *file, void *iter) { \
|
|
seq_printf(file, "%d\n", name); \
|
|
return 0; \
|
|
} \
|
|
static ssize_t name##_seq_write(struct file *file, const char __user *buffer, size_t count, loff_t *off) { \
|
|
return count; \
|
|
} \
|
|
static int name##_debugfs_open(struct inode *inode, struct file *file) { \
|
|
return single_open(file, name##_seq_show, inode->i_private); \
|
|
} \
|
|
static const struct file_operations name##_debugfs_fops = { \
|
|
.owner = THIS_MODULE, \
|
|
.open = name##_debugfs_open, \
|
|
.read = seq_read, \
|
|
.write = name##_seq_write, \
|
|
.llseek = seq_lseek, \
|
|
.release = single_release, \
|
|
};
|
|
|
|
//------------------------- normal nodes
|
|
DEF_NODE(level)
|
|
DEF_NODE(setway)
|
|
DEF_NODE(src_mset)
|
|
DEF_NODE(chk)
|
|
DEF_NODE(pmu)
|
|
DEF_NODE(dump)
|
|
DEF_NODE(flush)
|
|
DEF_NODE(align)
|
|
DEF_NODE(val)
|
|
DEF_NODE(start_size)
|
|
DEF_NODE(end_size)
|
|
DEF_NODE(func)
|
|
DEF_NODE(type)
|
|
DEF_NODE(core)
|
|
|
|
//------------------------- special nodes
|
|
// event
|
|
static int event_seq_show(struct seq_file *file, void *unused)
|
|
{
|
|
u32 val = read_pmnc();
|
|
seq_printf(file, "0x%x, 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
|
|
val,
|
|
events[0],
|
|
events[1],
|
|
events[2],
|
|
events[3],
|
|
events[4],
|
|
events[5],
|
|
events[6]);
|
|
return 0;
|
|
}
|
|
static ssize_t event_seq_write(struct file *file, const char __user *buffer, size_t count, loff_t *off)
|
|
{
|
|
char buf[128];
|
|
count = (count > 128)? 128 : count;
|
|
if (copy_from_user(buf, buffer, count) != 0)
|
|
return -EFAULT;
|
|
sscanf(buf, "%x %x %x %x %x %x %x",
|
|
&events[0],
|
|
&events[1],
|
|
&events[2],
|
|
&events[3],
|
|
&events[4],
|
|
&events[5],
|
|
&events[6]);
|
|
return count;
|
|
}
|
|
static int event_debugfs_open(struct inode *inode, struct file *file) {
|
|
return single_open(file, event_seq_show, inode->i_private);
|
|
}
|
|
static const struct file_operations event_debugfs_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = event_debugfs_open,
|
|
.read = seq_read,
|
|
.write = event_seq_write,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
// run
|
|
static int run_seq_show(struct seq_file *file, void *unused)
|
|
{
|
|
if (nc_memcpy_done == 0) {
|
|
seq_printf(file, "NO RESULT\n");
|
|
} else {
|
|
seq_printf(file, "%s", nc_memcpy_result_buf); // PRINT RESULT
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
static ssize_t run_seq_write(struct file *file, const char __user *buffer, size_t count, loff_t *off)
|
|
{
|
|
char buf[10];
|
|
count = (count > 10)? 10 : count;
|
|
if (copy_from_user(buf, buffer, count) != 0)
|
|
return -EFAULT;
|
|
sscanf(buf, "%d", &run);
|
|
if (run == 1) {
|
|
test_thread_init();
|
|
} else {
|
|
test_thread_exit();
|
|
}
|
|
return count;
|
|
}
|
|
static int run_debugfs_open(struct inode *inode, struct file *file) {
|
|
return single_open(file, run_seq_show, inode->i_private);
|
|
}
|
|
static const struct file_operations run_debugfs_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = run_debugfs_open,
|
|
.read = seq_read,
|
|
.write = run_seq_write,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
// info
|
|
#define PRINT_NODE(name) \
|
|
seq_printf(file, #name"=%d\n", name);
|
|
/*
|
|
DEF_NODE(level)
|
|
DEF_NODE(setway)
|
|
DEF_NODE(src_mset)
|
|
DEF_NODE(chk)
|
|
DEF_NODE(pmu)
|
|
DEF_NODE(dump)
|
|
DEF_NODE(flush)
|
|
DEF_NODE(align)
|
|
DEF_NODE(val)
|
|
DEF_NODE(start_size)
|
|
DEF_NODE(end_size)
|
|
DEF_NODE(func)
|
|
DEF_NODE(type)
|
|
DEF_NODE(core)
|
|
*/
|
|
static int info_seq_show(struct seq_file *file, void *unused)
|
|
{
|
|
int i;
|
|
seq_printf(file, "type_list:\n");
|
|
for (i = 1; i < MT_COUNT; i++)
|
|
seq_printf(file, "%d. %s\n", i, memtype_str[i]);
|
|
|
|
seq_printf(file, "func_list:\n");
|
|
for (i = 1; i < FT_COUNT; i++)
|
|
seq_printf(file, "%d. %s\n", i, functype_str[i]);
|
|
PRINT_NODE(core)
|
|
PRINT_NODE(func)
|
|
PRINT_NODE(type)
|
|
PRINT_NODE(start_size)
|
|
PRINT_NODE(end_size)
|
|
PRINT_NODE(src_mset)
|
|
PRINT_NODE(align)
|
|
PRINT_NODE(pmu)
|
|
PRINT_NODE(flush)
|
|
PRINT_NODE(chk)
|
|
return 0;
|
|
}
|
|
static ssize_t info_seq_write(struct file *file, const char __user *buffer, size_t count, loff_t *off) {
|
|
char buf[20];
|
|
count = (count > 20)? 20 : count;
|
|
if (copy_from_user(buf, buffer, count) != 0)
|
|
return -EFAULT;
|
|
return count;
|
|
}
|
|
static int info_debugfs_open(struct inode *inode, struct file *file) {
|
|
return single_open(file, info_seq_show, inode->i_private);
|
|
}
|
|
static const struct file_operations info_debugfs_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = info_debugfs_open,
|
|
.read = seq_read,
|
|
.write = info_seq_write,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
// end of sysfs nodes
|
|
/*------------------------------------------------------------------*/
|
|
|
|
//-------------------
|
|
// main
|
|
//-------------------
|
|
int exynos_perf_ncmemcpy_init(struct platform_device *pdev)
|
|
{
|
|
struct device_node *dn = pdev->dev.of_node;
|
|
struct dentry *root, *d;
|
|
|
|
memcpy_dev = &pdev->dev;
|
|
|
|
of_property_read_u32(dn, "cal-id-mif", &cal_id_mif);
|
|
of_property_read_u32(dn, "devfreq-mif", &devfreq_mif);
|
|
|
|
root = debugfs_create_dir("exynos_perf_ncmemcpy", NULL);
|
|
if (!root) {
|
|
pr_err("%s: create debugfs\n", __FILE__);
|
|
return -ENOMEM;
|
|
}
|
|
d = debugfs_create_file("level", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&level_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("setway", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&setway_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("src_mset", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&src_mset_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("chk", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&chk_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("pmu", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&pmu_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("dump", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&dump_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("flush", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&flush_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("align", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&align_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("val", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&val_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("start_size", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&start_size_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("end_size", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&end_size_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("func", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&func_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("type", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&type_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("core", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&core_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("event", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&event_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("run", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&run_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
d = debugfs_create_file("info", S_IRUSR, root,
|
|
(unsigned int *)0,
|
|
&info_debugfs_fops);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|