| /** |
| * 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); |