Created
June 24, 2014 19:40
-
-
Save teqdruid/cb095c58fef94f4aad23 to your computer and use it in GitHub Desktop.
Zynq PMU initialization module
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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