mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-01-19 12:24:32 +00:00
295 lines
7.7 KiB
C
Executable File
295 lines
7.7 KiB
C
Executable File
/* Copyright 2013-2015 ARM Limited
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
|
|
/*
|
|
* pmu_logger.c - Kernel module to log the CCI PMU counters
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/timer.h>
|
|
#include <asm/io.h>
|
|
|
|
#define MODULE_NAME "cci_pmu_logger"
|
|
|
|
// CCI_BASE needs to be modified to point to the mapped location of CCI in
|
|
// memory on your device.
|
|
#define CCI_BASE 0x2C090000 // TC2
|
|
//#define CCI_BASE 0x10D20000
|
|
#define CCI_SIZE 0x00010000
|
|
|
|
#define PMCR 0x100
|
|
|
|
#define PMCR_CEN (1 << 0)
|
|
#define PMCR_RST (1 << 1)
|
|
#define PMCR_CCR (1 << 2)
|
|
#define PMCR_CCD (1 << 3)
|
|
#define PMCR_EX (1 << 4)
|
|
#define PMCR_DP (1 << 5)
|
|
|
|
#define CC_BASE 0x9000
|
|
#define PC0_BASE 0xA000
|
|
#define PC1_BASE 0xB000
|
|
#define PC2_BASE 0xC000
|
|
#define PC3_BASE 0xD000
|
|
|
|
#define PC_ESR 0x0
|
|
#define CNT_VALUE 0x4
|
|
#define CNT_CONTROL 0x8
|
|
|
|
#define CNT_ENABLE (1 << 0)
|
|
|
|
u32 counter0_event = 0x6A;
|
|
u32 counter1_event = 0x63;
|
|
u32 counter2_event = 0x8A;
|
|
u32 counter3_event = 0x83;
|
|
|
|
u32 enable_console = 0;
|
|
u32 enable_ftrace = 1;
|
|
|
|
void *cci_base = 0;
|
|
|
|
static struct dentry *module_debugfs_root;
|
|
static int enabled = false;
|
|
|
|
u32 delay = 10; //jiffies. This translates to 1 sample every 100 ms
|
|
struct timer_list timer;
|
|
|
|
static void call_after_delay(void)
|
|
{
|
|
timer.expires = jiffies + delay;
|
|
add_timer(&timer);
|
|
}
|
|
|
|
|
|
static void setup_and_call_after_delay(void (*fn)(unsigned long))
|
|
{
|
|
init_timer(&timer);
|
|
timer.data = (unsigned long)&timer;
|
|
timer.function = fn;
|
|
|
|
call_after_delay();
|
|
}
|
|
|
|
static void print_counter_configuration(void)
|
|
{
|
|
if (enable_ftrace)
|
|
trace_printk("Counter_0: %02x Counter_1: %02x Counter_2: %02x Counter_3: %02x\n", \
|
|
counter0_event, counter1_event, counter2_event, counter3_event);
|
|
|
|
if (enable_console)
|
|
printk("Counter_0: %02x Counter_1: %02x Counter_2: %02x Counter_3: %02x\n", \
|
|
counter0_event, counter1_event, counter2_event, counter3_event);
|
|
}
|
|
|
|
static void initialize_cci_pmu(void)
|
|
{
|
|
u32 val;
|
|
|
|
// Select the events counted
|
|
iowrite32(counter0_event, cci_base + PC0_BASE + PC_ESR);
|
|
iowrite32(counter1_event, cci_base + PC1_BASE + PC_ESR);
|
|
iowrite32(counter2_event, cci_base + PC2_BASE + PC_ESR);
|
|
iowrite32(counter3_event, cci_base + PC3_BASE + PC_ESR);
|
|
|
|
// Enable the individual PMU counters
|
|
iowrite32(CNT_ENABLE, cci_base + PC0_BASE + CNT_CONTROL);
|
|
iowrite32(CNT_ENABLE, cci_base + PC1_BASE + CNT_CONTROL);
|
|
iowrite32(CNT_ENABLE, cci_base + PC2_BASE + CNT_CONTROL);
|
|
iowrite32(CNT_ENABLE, cci_base + PC3_BASE + CNT_CONTROL);
|
|
iowrite32(CNT_ENABLE, cci_base + CC_BASE + CNT_CONTROL);
|
|
|
|
// Reset the counters and configure the Cycle Count Divider
|
|
val = ioread32(cci_base + PMCR);
|
|
iowrite32(val | PMCR_RST | PMCR_CCR | PMCR_CCD, cci_base + PMCR);
|
|
}
|
|
|
|
static void enable_cci_pmu_counters(void)
|
|
{
|
|
u32 val = ioread32(cci_base + PMCR);
|
|
iowrite32(val | PMCR_CEN, cci_base + PMCR);
|
|
}
|
|
|
|
static void disable_cci_pmu_counters(void)
|
|
{
|
|
u32 val = ioread32(cci_base + PMCR);
|
|
iowrite32(val & ~PMCR_CEN, cci_base + PMCR);
|
|
}
|
|
|
|
static void trace_values(unsigned long arg)
|
|
{
|
|
u32 cycles;
|
|
u32 counter[4];
|
|
|
|
cycles = ioread32(cci_base + CC_BASE + CNT_VALUE);
|
|
counter[0] = ioread32(cci_base + PC0_BASE + CNT_VALUE);
|
|
counter[1] = ioread32(cci_base + PC1_BASE + CNT_VALUE);
|
|
counter[2] = ioread32(cci_base + PC2_BASE + CNT_VALUE);
|
|
counter[3] = ioread32(cci_base + PC3_BASE + CNT_VALUE);
|
|
|
|
if (enable_ftrace)
|
|
trace_printk("Cycles: %08x Counter_0: %08x"
|
|
" Counter_1: %08x Counter_2: %08x Counter_3: %08x\n", \
|
|
cycles, counter[0], counter[1], counter[2], counter[3]);
|
|
|
|
if (enable_console)
|
|
printk("Cycles: %08x Counter_0: %08x"
|
|
" Counter_1: %08x Counter_2: %08x Counter_3: %08x\n", \
|
|
cycles, counter[0], counter[1], counter[2], counter[3]);
|
|
|
|
if (enabled) {
|
|
u32 val;
|
|
// Reset the counters
|
|
val = ioread32(cci_base + PMCR);
|
|
iowrite32(val | PMCR_RST | PMCR_CCR, cci_base + PMCR);
|
|
|
|
call_after_delay();
|
|
}
|
|
}
|
|
|
|
static ssize_t read_control(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
|
{
|
|
char status[16];
|
|
/* printk(KERN_DEBUG "%s\n", __func__); */
|
|
|
|
if (enabled)
|
|
snprintf(status, 16, "enabled\n");
|
|
else
|
|
snprintf(status, 16, "disabled\n");
|
|
|
|
return simple_read_from_buffer(buf, count, ppos, status, strlen(status));
|
|
}
|
|
|
|
static ssize_t write_control(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
|
|
{
|
|
if (enabled) {
|
|
disable_cci_pmu_counters();
|
|
enabled = false;
|
|
} else {
|
|
initialize_cci_pmu();
|
|
enable_cci_pmu_counters();
|
|
enabled = true;
|
|
|
|
print_counter_configuration();
|
|
setup_and_call_after_delay(trace_values);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t read_values(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
|
{
|
|
char values[256];
|
|
/* u32 val; */
|
|
|
|
snprintf(values, 256, "Cycles: %08x Counter_0: %08x"
|
|
" Counter_1: %08x Counter_2: %08x Counter_3: %08x\n", \
|
|
ioread32(cci_base + CC_BASE + CNT_VALUE), \
|
|
ioread32(cci_base + PC0_BASE + CNT_VALUE), \
|
|
ioread32(cci_base + PC1_BASE + CNT_VALUE), \
|
|
ioread32(cci_base + PC2_BASE + CNT_VALUE), \
|
|
ioread32(cci_base + PC3_BASE + CNT_VALUE));
|
|
|
|
return simple_read_from_buffer(buf, count, ppos, values, strlen(values));
|
|
}
|
|
|
|
static const struct file_operations control_fops = {
|
|
.owner = THIS_MODULE,
|
|
.read = read_control,
|
|
.write = write_control,
|
|
};
|
|
|
|
static const struct file_operations value_fops = {
|
|
.owner = THIS_MODULE,
|
|
.read = read_values,
|
|
};
|
|
|
|
static int __init pmu_logger_init(void)
|
|
{
|
|
struct dentry *retval;
|
|
|
|
module_debugfs_root = debugfs_create_dir(MODULE_NAME, NULL);
|
|
if (!module_debugfs_root || IS_ERR(module_debugfs_root)) {
|
|
printk(KERN_ERR "error creating debugfs dir.\n");
|
|
goto out;
|
|
}
|
|
|
|
retval = debugfs_create_file("control", S_IRUGO | S_IWUGO, module_debugfs_root, NULL, &control_fops);
|
|
if (!retval)
|
|
goto out;
|
|
|
|
retval = debugfs_create_file("values", S_IRUGO, module_debugfs_root, NULL, &value_fops);
|
|
if (!retval)
|
|
goto out;
|
|
|
|
retval = debugfs_create_bool("enable_console", S_IRUGO | S_IWUGO, module_debugfs_root, &enable_console);
|
|
if (!retval)
|
|
goto out;
|
|
|
|
retval = debugfs_create_bool("enable_ftrace", S_IRUGO | S_IWUGO, module_debugfs_root, &enable_ftrace);
|
|
if (!retval)
|
|
goto out;
|
|
|
|
retval = debugfs_create_u32("period_jiffies", S_IRUGO | S_IWUGO, module_debugfs_root, &delay);
|
|
if (!retval)
|
|
goto out;
|
|
|
|
retval = debugfs_create_x32("counter0", S_IRUGO | S_IWUGO, module_debugfs_root, &counter0_event);
|
|
if (!retval)
|
|
goto out;
|
|
retval = debugfs_create_x32("counter1", S_IRUGO | S_IWUGO, module_debugfs_root, &counter1_event);
|
|
if (!retval)
|
|
goto out;
|
|
retval = debugfs_create_x32("counter2", S_IRUGO | S_IWUGO, module_debugfs_root, &counter2_event);
|
|
if (!retval)
|
|
goto out;
|
|
retval = debugfs_create_x32("counter3", S_IRUGO | S_IWUGO, module_debugfs_root, &counter3_event);
|
|
if (!retval)
|
|
goto out;
|
|
|
|
cci_base = ioremap(CCI_BASE, CCI_SIZE);
|
|
if (!cci_base)
|
|
goto out;
|
|
|
|
printk(KERN_INFO "CCI PMU Logger loaded.\n");
|
|
return 0;
|
|
|
|
out:
|
|
debugfs_remove_recursive(module_debugfs_root);
|
|
return 1;
|
|
}
|
|
|
|
static void __exit pmu_logger_exit(void)
|
|
{
|
|
if (module_debugfs_root) {
|
|
debugfs_remove_recursive(module_debugfs_root);
|
|
module_debugfs_root = NULL;
|
|
}
|
|
if (cci_base)
|
|
iounmap(cci_base);
|
|
|
|
printk(KERN_INFO "CCI PMU Logger removed.\n");
|
|
}
|
|
|
|
module_init(pmu_logger_init);
|
|
module_exit(pmu_logger_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Punit Agrawal");
|
|
MODULE_DESCRIPTION("logger for CCI PMU counters");
|