Skip to content

Instantly share code, notes, and snippets.

@teqdruid
Created June 24, 2014 19:40
Show Gist options
  • Save teqdruid/cb095c58fef94f4aad23 to your computer and use it in GitHub Desktop.
Save teqdruid/cb095c58fef94f4aad23 to your computer and use it in GitHub Desktop.
Zynq PMU initialization module
/*
* Kernel module to initialize counters
*/
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_ERR */
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include "v7_pmu.h"
#define OFF_PERF0 0xf8891000
#define OFF_PERF1 0xf8893000
#define CR 0xE04
#define USEREN 0xE08
static void __iomem* perf0;
static void __iomem* perf1;
unsigned int events[] = {
0x68, 0x07, 0x0C, 0x0D, 0x0F, 0x12
};
void dump_regs(void) {
u32 val;
unsigned int cnt;
unsigned num_ctrs = 6;
printk(KERN_INFO "PMNC Core %u registers dump:\n", smp_processor_id());
asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
printk(KERN_INFO "PMNC =0x%08x\n", val);
asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
printk(KERN_INFO "CNTENS=0x%08x\n", val);
asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));
printk(KERN_INFO "INTENS=0x%08x\n", val);
asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
printk(KERN_INFO "FLAGS =0x%08x\n", val);
asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));
printk(KERN_INFO "SELECT=0x%08x\n", val);
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
printk(KERN_INFO "CCNT =0x%08x\n", val);
asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r" (val));
printk(KERN_INFO "USREN =0x%08x\n", val);
asm volatile("mrc p14, 0, %0, c7, c14, 6" : "=r" (val));
printk(KERN_INFO "DBGAUTHSTATUS: %x", val);
for (cnt = 0; cnt < num_ctrs; cnt++) {
printk(KERN_INFO "CNT[%d] count =0x%08x\n",
cnt, read_pmn_int(cnt));
asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));
printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n",
cnt, val);
}
}
void initCountersLocal(void* __dummy) {
uint32_t a, b, c, d;
int i;
unsigned num_ctrs = 6;
disable_pmu();
disable_ccnt();
reset_ccnt();
reset_pmn();
// Reset overflow flags
write_flags(0xFFFFFFFF);
write_ccnt(0x0);
for (i=0; i<num_ctrs; i++) {
pmn_config(i, events[i]);
}
dump_regs();
ccnt_divider(0);
enable_ccnt_irq();
for (i = 0; i < num_ctrs; i++) {
enable_pmn(i);
enable_pmn_irq(i);
}
enable_ccnt();
enable_pmu();
a = read_ccnt_int();
b = ioread32(perf0+0x7C);
c = ioread32(perf1+0x7C);
d = read_ccnt_int();
printk(KERN_ERR "ENA FLAGS : %8x\n", read_flags());
printk(KERN_ERR "ENA CCNT CP: %8x\n", a);
printk(KERN_ERR "ENA CCNT MM: %8x\n", b);
printk(KERN_ERR "ENA CCNT MM: %8x\n", c);
printk(KERN_ERR "ENA CCNT CP: %8x\n", d);
}
void disableCountersLocal(void* __dummy) {
uint32_t a, b, c, d;
a = read_ccnt_int();
b = ioread32(perf0+0x7C);
c = ioread32(perf1+0x7C);
d = read_ccnt_int();
printk(KERN_ERR "DIS FLAGS : %8x\n", read_flags());
printk(KERN_ERR "DIS CCNT CP: %8x\n", a);
printk(KERN_ERR "DIS CCNT MM: %8x\n", b);
printk(KERN_ERR "DIS CCNT MM: %8x\n", c);
printk(KERN_ERR "DIS CCNT CP: %8x\n", d);
disable_pmu();
}
static struct platform_device* plat_device;
static int cpu_pmu_device_probe(struct platform_device *pdev)
{
int ret = 0;
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
plat_device = pdev;
on_each_cpu(initCountersLocal, NULL, 1);
return ret;
}
static int armpmu_runtime_resume(struct device *dev)
{
return 0;
}
static int armpmu_runtime_suspend(struct device *dev)
{
return 0;
}
const struct dev_pm_ops armpmuhw_dev_pm_ops = {
SET_RUNTIME_PM_OPS(armpmu_runtime_suspend, armpmu_runtime_resume, NULL)
};
/*
* PMU platform driver and devicetree bindings.
*/
static struct of_device_id cpu_pmu_of_device_ids[] = {
{.compatible = "arm,cortex-a9-pmu-hwsampler", .data = NULL},
{},
};
static struct platform_device_id cpu_pmu_plat_device_ids[] = {
{.name = "arm-pmu-hwsampler"},
{},
};
static struct platform_driver cpu_pmu_driver = {
.driver = {
.name = "arm-pmu-hwsampler",
.pm = &armpmuhw_dev_pm_ops,
.of_match_table = cpu_pmu_of_device_ids,
},
.probe = cpu_pmu_device_probe,
.id_table = cpu_pmu_plat_device_ids,
};
int init_module(void)
{
int err = 0;
perf0 = ioremap(OFF_PERF0, 4096);
perf1 = ioremap(OFF_PERF1, 4096);
printk(KERN_ERR "Initializing PMU... v:%p\n", perf0);
plat_device = NULL;
platform_driver_probe(&cpu_pmu_driver, cpu_pmu_device_probe);
return err;
}
void cleanup_module(void)
{
printk(KERN_ERR "Shutting down PMU...\n");
on_each_cpu(disableCountersLocal, NULL, 1);
if (plat_device)
pm_runtime_put_sync(&plat_device->dev);
iounmap(perf0);
iounmap(perf1);
platform_driver_unregister(&cpu_pmu_driver);
}
MODULE_LICENSE("GPL");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment