Merge tag 'v3.10.41' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / drivers / gator / gator_events_ccn-504.c
index b91a9a149b90546a8133ecef8c92137da6b9a6d0..024ffc2856aa0b7312ad84a89f0d680494c27bf4 100644 (file)
@@ -1,28 +1,21 @@
 /**
- * Copyright (C) ARM Limited 2013. All rights reserved.
+ * Copyright (C) ARM Limited 2013-2014. 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.
  */
 
-/*******************************************************************************
- * WARNING: This code is an experimental implementation of the CCN-504 hardware
- * counters which has not been tested on the hardware. Commented debug
- * statements are present and can be uncommented for diagnostic purposes.
- ******************************************************************************/
-
 #include <linux/io.h>
 #include <linux/module.h>
 
 #include "gator.h"
 
-#define PERIPHBASE 0x2E000000
-
 #define NUM_REGIONS 256
 #define REGION_SIZE (64*1024)
 #define REGION_DEBUG 1
 #define REGION_XP 64
+#define NUM_XPS 11
 
 // DT (Debug) region
 #define PMEVCNTSR0    0x0150
 
 // XP region
 #define DT_CONFIG     0x0300
+#define DT_CONTROL    0x0370
 
 // Multiple
 #define PMU_EVENT_SEL 0x0600
 #define OLY_ID        0xFF00
 
 #define CCNT 4
-#define CNTMAX (4 + 1)
+#define CNTMAX (CCNT + 1)
 
 #define get_pmu_event_id(event) (((event) >> 0) & 0xFF)
 #define get_node_type(event) (((event) >> 8) & 0xFF)
 #define get_region(event) (((event) >> 16) & 0xFF)
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+
+// From kernel/params.c
+#define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn)              \
+       int param_set_##name(const char *val, struct kernel_param *kp)  \
+       {                                                               \
+               tmptype l;                                              \
+               int ret;                                                \
+                                                                       \
+               if (!val) return -EINVAL;                               \
+               ret = strtolfn(val, 0, &l);                             \
+               if (ret == -EINVAL || ((type)l != l))                   \
+                       return -EINVAL;                                 \
+               *((type *)kp->arg) = l;                                 \
+               return 0;                                               \
+       }                                                               \
+       int param_get_##name(char *buffer, struct kernel_param *kp)     \
+       {                                                               \
+               return sprintf(buffer, format, *((type *)kp->arg));     \
+       }
+
+#else
+
+// From kernel/params.c
+#define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn)              \
+       int param_set_##name(const char *val, const struct kernel_param *kp) \
+       {                                                               \
+               tmptype l;                                              \
+               int ret;                                                \
+                                                                       \
+               ret = strtolfn(val, 0, &l);                             \
+               if (ret < 0 || ((type)l != l))                          \
+                       return ret < 0 ? ret : -EINVAL;                 \
+               *((type *)kp->arg) = l;                                 \
+               return 0;                                               \
+       }                                                               \
+       int param_get_##name(char *buffer, const struct kernel_param *kp) \
+       {                                                               \
+               return scnprintf(buffer, PAGE_SIZE, format,             \
+                               *((type *)kp->arg));                    \
+       }                                                               \
+       struct kernel_param_ops param_ops_##name = {                    \
+               .set = param_set_##name,                                \
+               .get = param_get_##name,                                \
+       };                                                              \
+       EXPORT_SYMBOL(param_set_##name);                                \
+       EXPORT_SYMBOL(param_get_##name);                                \
+       EXPORT_SYMBOL(param_ops_##name)
+
+#endif
+
+STANDARD_PARAM_DEF(u64, u64, "%llu", u64, strict_strtoull);
+
+// From include/linux/moduleparam.h
+#define param_check_u64(name, p) __param_check(name, p, u64)
+
 MODULE_PARM_DESC(ccn504_addr, "CCN-504 physical base address");
-static unsigned long ccn504_addr = 0;
-module_param(ccn504_addr, ulong, 0444);
+static u64 ccn504_addr = 0;
+module_param(ccn504_addr, u64, 0444);
 
 static void __iomem *gator_events_ccn504_base;
+static bool gator_events_ccn504_global_enabled;
 static unsigned long gator_events_ccn504_enabled[CNTMAX];
 static unsigned long gator_events_ccn504_event[CNTMAX];
 static unsigned long gator_events_ccn504_key[CNTMAX];
 static int gator_events_ccn504_buffer[2*CNTMAX];
+static int gator_events_ccn504_prev[CNTMAX];
 
 static void gator_events_ccn504_create_shutdown(void)
 {
@@ -96,7 +148,6 @@ static void gator_events_ccn504_set_dt_config(int xp_node_id, int event_num, int
 
        dt_config = readl(gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG);
        dt_config |= (value + event_num) << (4*event_num);
-       //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, dt_config, (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG);
        writel(dt_config, gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG);
 }
 
@@ -104,6 +155,20 @@ static int gator_events_ccn504_start(void)
 {
        int i;
 
+       gator_events_ccn504_global_enabled = 0;
+       for (i = 0; i < CNTMAX; ++i) {
+               if (gator_events_ccn504_enabled[i]) {
+                       gator_events_ccn504_global_enabled = 1;
+                       break;
+               }
+       }
+
+       if (!gator_events_ccn504_global_enabled) {
+               return 0;
+       }
+
+       memset(&gator_events_ccn504_prev, 0x80, sizeof(gator_events_ccn504_prev));
+
        // Disable INTREQ on overflow
        // [6] ovfl_intr_en = 0
        // perhaps set to 1?
@@ -112,9 +177,22 @@ static int gator_events_ccn504_start(void)
        // [4:1] cntcfg = 0
        // Enable PMU features
        // [0] pmu_en = 1
-       //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, 0x1, REGION_DEBUG*REGION_SIZE + PMCR);
        writel(0x1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMCR);
 
+       // Configure the XPs
+       for (i = 0; i < NUM_XPS; ++i) {
+               int dt_control;
+
+               // Pass on all events
+               writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG);
+
+               // Enable PMU capability
+               // [0] dt_enable = 1
+               dt_control = readl(gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONTROL);
+               dt_control |= 0x1;
+               writel(dt_control, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONTROL);
+       }
+
        // Assume no other pmu_event_sel registers are set
 
        // cycle counter does not need to be enabled
@@ -134,15 +212,14 @@ static int gator_events_ccn504_start(void)
                pmu_event_id = get_pmu_event_id(gator_events_ccn504_event[i]);
                node_type = get_node_type(gator_events_ccn504_event[i]);
                region = get_region(gator_events_ccn504_event[i]);
-               //printk(KERN_ERR "%s(%s:%i) pmu_event_id: %x node_type: %x region: %x\n", __FUNCTION__, __FILE__, __LINE__, pmu_event_id, node_type, region);
 
                // Verify the node_type
                oly_id_whole = readl(gator_events_ccn504_base + region*REGION_SIZE + OLY_ID);
                oly_id = oly_id_whole & 0x1F;
                node_id = (oly_id_whole >> 8) & 0x7F;
                if ((oly_id != node_type) ||
-                               ((node_type == 0x16) && ((oly_id == 0x14) || (oly_id == 0x15) || (oly_id == 0x16) || (oly_id == 0x18) || (oly_id == 0x19) || (oly_id == 0x1A)))) {
-                       printk(KERN_ERR "%s(%s:%i) oly_id is %x expected %x\n", __FUNCTION__, __FILE__, __LINE__, oly_id, node_type);
+                               ((node_type == 0x16) && ((oly_id != 0x14) && (oly_id != 0x15) && (oly_id != 0x16) && (oly_id != 0x18) && (oly_id != 0x19) && (oly_id != 0x1A)))) {
+                       printk(KERN_ERR "gator: oly_id is 0x%x expected 0x%x\n", oly_id, node_type);
                        return -1;
                }
 
@@ -160,7 +237,6 @@ static int gator_events_ccn504_start(void)
                        gator_events_ccn504_set_dt_config(node_id/2, i, (node_id & 1) == 0 ? 0x8 : 0xC);
                        break;
                }
-               //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, pmu_event_sel, region*REGION_SIZE + PMU_EVENT_SEL);
                writel(pmu_event_sel, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL);
        }
 
@@ -171,21 +247,25 @@ static void gator_events_ccn504_stop(void)
 {
        int i;
 
+       if (!gator_events_ccn504_global_enabled) {
+               return;
+       }
+
        // cycle counter does not need to be disabled
        for (i = 0; i < CCNT; ++i) {
-               int node_type;
                int region;
 
-               node_type = get_node_type(gator_events_ccn504_event[i]);
+               if (!gator_events_ccn504_enabled[i]) {
+                       continue;
+               }
+
                region = get_region(gator_events_ccn504_event[i]);
 
-               //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, 0, region*REGION_SIZE + PMU_EVENT_SEL);
                writel(0, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL);
        }
 
        // Clear dt_config
-       for (i = 0; i < 11; ++i) {
-               //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, 0, (REGION_XP + i)*REGION_SIZE + DT_CONFIG);
+       for (i = 0; i < NUM_XPS; ++i) {
                writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG);
        }
 }
@@ -194,27 +274,20 @@ static int gator_events_ccn504_read(int **buffer)
 {
        int i;
        int len = 0;
+       int value;
 
-       if (!on_primary_core()) {
+       if (!on_primary_core() || !gator_events_ccn504_global_enabled) {
                return 0;
        }
 
        // Verify the pmsr register is zero
-       //i = 0;
-       while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) != 0) {
-               //++i;
-       }
-       //printk(KERN_ERR "%s(%s:%i) %i\n", __FUNCTION__, __FILE__, __LINE__, i);
+       while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) != 0);
 
        // Request a PMU snapshot
        writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_REQ);
 
        // Wait for the snapshot
-       //i = 0;
-       while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) == 0) {
-               //++i;
-       }
-       //printk(KERN_ERR "%s(%s:%i) %i\n", __FUNCTION__, __FILE__, __LINE__, i);
+       while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) == 0);
 
        // Read the shadow registers
        for (i = 0; i < CNTMAX; ++i) {
@@ -222,8 +295,12 @@ static int gator_events_ccn504_read(int **buffer)
                        continue;
                }
 
-               gator_events_ccn504_buffer[len++] = gator_events_ccn504_key[i];
-               gator_events_ccn504_buffer[len++] = readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + (i == CCNT ? PMCCNTRSR : PMEVCNTSR0 + 8*i));
+               value = readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + (i == CCNT ? PMCCNTRSR : PMEVCNTSR0 + 8*i));
+               if (gator_events_ccn504_prev[i] != 0x80808080) {
+                       gator_events_ccn504_buffer[len++] = gator_events_ccn504_key[i];
+                       gator_events_ccn504_buffer[len++] = value - gator_events_ccn504_prev[i];
+               }
+               gator_events_ccn504_prev[i] = value;
 
                // Are the counters registers cleared when read? Is that what the cntr_rst bit on the pmcr register does?
        }
@@ -231,18 +308,10 @@ static int gator_events_ccn504_read(int **buffer)
        // Clear the PMU snapshot status
        writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_CLR);
 
-       return len;
-}
-
-static void __maybe_unused gator_events_ccn504_enumerate(int pos, int size)
-{
-       int i;
-       u32 oly_id;
+       if (buffer)
+               *buffer = gator_events_ccn504_buffer;
 
-       for (i = pos; i < pos + size; ++i) {
-               oly_id = readl(gator_events_ccn504_base + i*REGION_SIZE + OLY_ID);
-               printk(KERN_ERR "%s(%s:%i) %i %08x\n", __FUNCTION__, __FILE__, __LINE__, i, oly_id);
-       }
+       return len;
 }
 
 static struct gator_interface gator_events_ccn504_interface = {
@@ -263,36 +332,9 @@ int gator_events_ccn504_init(void)
 
        gator_events_ccn504_base = ioremap(ccn504_addr, NUM_REGIONS*REGION_SIZE);
        if (gator_events_ccn504_base == NULL) {
-               printk(KERN_ERR "%s(%s:%i) ioremap returned NULL\n", __FUNCTION__, __FILE__, __LINE__);
+               printk(KERN_ERR "gator: ioremap returned NULL\n");
                return -1;
        }
-       //printk(KERN_ERR "%s(%s:%i)\n", __FUNCTION__, __FILE__, __LINE__);
-
-       // Test - can memory be read
-       {
-               //gator_events_ccn504_enumerate(0, NUM_REGIONS);
-
-#if 0
-               // DT
-               gator_events_ccn504_enumerate(1, 1);
-               // HN-F
-               gator_events_ccn504_enumerate(32, 8);
-               // XP
-               gator_events_ccn504_enumerate(64, 11);
-               // RN-I
-               gator_events_ccn504_enumerate(128, 1);
-               gator_events_ccn504_enumerate(130, 1);
-               gator_events_ccn504_enumerate(134, 1);
-               gator_events_ccn504_enumerate(140, 1);
-               gator_events_ccn504_enumerate(144, 1);
-               gator_events_ccn504_enumerate(148, 1);
-               // SBAS
-               gator_events_ccn504_enumerate(129, 1);
-               gator_events_ccn504_enumerate(137, 1);
-               gator_events_ccn504_enumerate(139, 1);
-               gator_events_ccn504_enumerate(147, 1);
-#endif
-       }
 
        for (i = 0; i < CNTMAX; ++i) {
                gator_events_ccn504_enabled[i] = 0;
@@ -302,5 +344,3 @@ int gator_events_ccn504_init(void)
 
        return gator_events_install(&gator_events_ccn504_interface);
 }
-
-gator_events_init(gator_events_ccn504_init);