blob: 21aa4a214d97c02c09a9dfdea253e0cce7e8ae0d [file] [log] [blame]
/**
* l2c310 (L2 Cache Controller) event counters for gator
*
* Copyright (C) ARM Limited 2010-2013. All rights reserved.
*
* 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/init.h>
#include <linux/io.h>
#include <linux/module.h>
#if defined(CONFIG_OF)
#include <linux/of.h>
#include <linux/of_address.h>
#endif
#include <asm/hardware/cache-l2x0.h>
#include "gator.h"
#define L2C310_COUNTERS_NUM 2
static struct {
unsigned long enabled;
unsigned long event;
unsigned long key;
} l2c310_counters[L2C310_COUNTERS_NUM];
static int l2c310_buffer[L2C310_COUNTERS_NUM * 2];
static void __iomem *l2c310_base;
static void gator_events_l2c310_reset_counters(void)
{
u32 val = readl(l2c310_base + L2X0_EVENT_CNT_CTRL);
val |= ((1 << L2C310_COUNTERS_NUM) - 1) << 1;
writel(val, l2c310_base + L2X0_EVENT_CNT_CTRL);
}
static int gator_events_l2c310_create_files(struct super_block *sb,
struct dentry *root)
{
int i;
for (i = 0; i < L2C310_COUNTERS_NUM; i++) {
char buf[16];
struct dentry *dir;
snprintf(buf, sizeof(buf), "L2C-310_cnt%d", i);
dir = gatorfs_mkdir(sb, root, buf);
if (WARN_ON(!dir))
return -1;
gatorfs_create_ulong(sb, dir, "enabled",
&l2c310_counters[i].enabled);
gatorfs_create_ulong(sb, dir, "event",
&l2c310_counters[i].event);
gatorfs_create_ro_ulong(sb, dir, "key",
&l2c310_counters[i].key);
}
return 0;
}
static int gator_events_l2c310_start(void)
{
static const unsigned long l2x0_event_cntx_cfg[L2C310_COUNTERS_NUM] = {
L2X0_EVENT_CNT0_CFG,
L2X0_EVENT_CNT1_CFG,
};
int i;
/* Counter event sources */
for (i = 0; i < L2C310_COUNTERS_NUM; i++)
writel((l2c310_counters[i].event & 0xf) << 2,
l2c310_base + l2x0_event_cntx_cfg[i]);
gator_events_l2c310_reset_counters();
/* Event counter enable */
writel(1, l2c310_base + L2X0_EVENT_CNT_CTRL);
return 0;
}
static void gator_events_l2c310_stop(void)
{
/* Event counter disable */
writel(0, l2c310_base + L2X0_EVENT_CNT_CTRL);
}
static int gator_events_l2c310_read(int **buffer)
{
static const unsigned long l2x0_event_cntx_val[L2C310_COUNTERS_NUM] = {
L2X0_EVENT_CNT0_VAL,
L2X0_EVENT_CNT1_VAL,
};
int i;
int len = 0;
if (!on_primary_core())
return 0;
for (i = 0; i < L2C310_COUNTERS_NUM; i++) {
if (l2c310_counters[i].enabled) {
l2c310_buffer[len++] = l2c310_counters[i].key;
l2c310_buffer[len++] = readl(l2c310_base +
l2x0_event_cntx_val[i]);
}
}
/* l2c310 counters are saturating, not wrapping in case of overflow */
gator_events_l2c310_reset_counters();
if (buffer)
*buffer = l2c310_buffer;
return len;
}
static struct gator_interface gator_events_l2c310_interface = {
.create_files = gator_events_l2c310_create_files,
.start = gator_events_l2c310_start,
.stop = gator_events_l2c310_stop,
.read = gator_events_l2c310_read,
};
#define L2C310_ADDR_PROBE (~0)
MODULE_PARM_DESC(l2c310_addr, "L2C310 physical base address (0 to disable)");
static unsigned long l2c310_addr = L2C310_ADDR_PROBE;
module_param(l2c310_addr, ulong, 0444);
static void __iomem *gator_events_l2c310_probe(void)
{
phys_addr_t variants[] = {
#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_S5PV310)
0x10502000,
#endif
#if defined(CONFIG_ARCH_OMAP4)
0x48242000,
#endif
#if defined(CONFIG_ARCH_TEGRA)
0x50043000,
#endif
#if defined(CONFIG_ARCH_U8500)
0xa0412000,
#endif
#if defined(CONFIG_ARCH_VEXPRESS)
0x1e00a000, // A9x4 core tile (HBI-0191)
0x2c0f0000, // New memory map tiles
#endif
};
int i;
void __iomem *base;
#if defined(CONFIG_OF)
struct device_node *node = of_find_all_nodes(NULL);
if (node) {
of_node_put(node);
node = of_find_compatible_node(NULL, NULL, "arm,pl310-cache");
base = of_iomap(node, 0);
of_node_put(node);
return base;
}
#endif
for (i = 0; i < ARRAY_SIZE(variants); i++) {
base = ioremap(variants[i], SZ_4K);
if (base) {
u32 cache_id = readl(base + L2X0_CACHE_ID);
if ((cache_id & 0xff0003c0) == 0x410000c0)
return base;
iounmap(base);
}
}
return NULL;
}
int gator_events_l2c310_init(void)
{
int i;
if (gator_cpuid() != CORTEX_A5 && gator_cpuid() != CORTEX_A9)
return -1;
if (l2c310_addr == L2C310_ADDR_PROBE)
l2c310_base = gator_events_l2c310_probe();
else if (l2c310_addr)
l2c310_base = ioremap(l2c310_addr, SZ_4K);
if (!l2c310_base)
return -1;
for (i = 0; i < L2C310_COUNTERS_NUM; i++) {
l2c310_counters[i].enabled = 0;
l2c310_counters[i].key = gator_events_get_key();
}
return gator_events_install(&gator_events_l2c310_interface);
}
gator_events_init(gator_events_l2c310_init);