Merge tag 'v3.10.28' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / drivers / gator / gator_events_ccn-504.c
1 /**
2  * Copyright (C) ARM Limited 2013. All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8
9 #include <linux/io.h>
10 #include <linux/module.h>
11
12 #include "gator.h"
13
14 #define NUM_REGIONS 256
15 #define REGION_SIZE (64*1024)
16 #define REGION_DEBUG 1
17 #define REGION_XP 64
18 #define NUM_XPS 11
19
20 // DT (Debug) region
21 #define PMEVCNTSR0    0x0150
22 #define PMCCNTRSR     0x0190
23 #define PMCR          0x01A8
24 #define PMSR          0x01B0
25 #define PMSR_REQ      0x01B8
26 #define PMSR_CLR      0x01C0
27
28 // XP region
29 #define DT_CONFIG     0x0300
30 #define DT_CONTROL    0x0370
31
32 // Multiple
33 #define PMU_EVENT_SEL 0x0600
34 #define OLY_ID        0xFF00
35
36 #define CCNT 4
37 #define CNTMAX (CCNT + 1)
38
39 #define get_pmu_event_id(event) (((event) >> 0) & 0xFF)
40 #define get_node_type(event) (((event) >> 8) & 0xFF)
41 #define get_region(event) (((event) >> 16) & 0xFF)
42
43 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
44
45 // From kernel/params.c
46 #define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn)       \
47         int param_set_##name(const char *val, struct kernel_param *kp)  \
48         {                                                               \
49                 tmptype l;                                              \
50                 int ret;                                                \
51                                                                         \
52                 if (!val) return -EINVAL;                               \
53                 ret = strtolfn(val, 0, &l);                             \
54                 if (ret == -EINVAL || ((type)l != l))                   \
55                         return -EINVAL;                                 \
56                 *((type *)kp->arg) = l;                                 \
57                 return 0;                                               \
58         }                                                               \
59         int param_get_##name(char *buffer, struct kernel_param *kp)     \
60         {                                                               \
61                 return sprintf(buffer, format, *((type *)kp->arg));     \
62         }
63
64 #else
65
66 // From kernel/params.c
67 #define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn)       \
68         int param_set_##name(const char *val, const struct kernel_param *kp) \
69         {                                                               \
70                 tmptype l;                                              \
71                 int ret;                                                \
72                                                                         \
73                 ret = strtolfn(val, 0, &l);                             \
74                 if (ret < 0 || ((type)l != l))                          \
75                         return ret < 0 ? ret : -EINVAL;                 \
76                 *((type *)kp->arg) = l;                                 \
77                 return 0;                                               \
78         }                                                               \
79         int param_get_##name(char *buffer, const struct kernel_param *kp) \
80         {                                                               \
81                 return scnprintf(buffer, PAGE_SIZE, format,             \
82                                 *((type *)kp->arg));                    \
83         }                                                               \
84         struct kernel_param_ops param_ops_##name = {                    \
85                 .set = param_set_##name,                                \
86                 .get = param_get_##name,                                \
87         };                                                              \
88         EXPORT_SYMBOL(param_set_##name);                                \
89         EXPORT_SYMBOL(param_get_##name);                                \
90         EXPORT_SYMBOL(param_ops_##name)
91
92 #endif
93
94 STANDARD_PARAM_DEF(u64, u64, "%llu", u64, strict_strtoull);
95
96 // From include/linux/moduleparam.h
97 #define param_check_u64(name, p) __param_check(name, p, u64)
98
99 MODULE_PARM_DESC(ccn504_addr, "CCN-504 physical base address");
100 static u64 ccn504_addr = 0;
101 module_param(ccn504_addr, u64, 0444);
102
103 static void __iomem *gator_events_ccn504_base;
104 static bool gator_events_ccn504_global_enabled;
105 static unsigned long gator_events_ccn504_enabled[CNTMAX];
106 static unsigned long gator_events_ccn504_event[CNTMAX];
107 static unsigned long gator_events_ccn504_key[CNTMAX];
108 static int gator_events_ccn504_buffer[2*CNTMAX];
109 static int gator_events_ccn504_prev[CNTMAX];
110
111 static void gator_events_ccn504_create_shutdown(void)
112 {
113         if (gator_events_ccn504_base != NULL) {
114                 iounmap(gator_events_ccn504_base);
115         }
116 }
117
118 static int gator_events_ccn504_create_files(struct super_block *sb, struct dentry *root)
119 {
120         struct dentry *dir;
121         int i;
122         char buf[32];
123
124         for (i = 0; i < CNTMAX; ++i) {
125                 if (i == CCNT) {
126                         snprintf(buf, sizeof(buf), "CCN-504_ccnt");
127                 } else {
128                         snprintf(buf, sizeof(buf), "CCN-504_cnt%i", i);
129                 }
130                 dir = gatorfs_mkdir(sb, root, buf);
131                 if (!dir) {
132                         return -1;
133                 }
134
135                 gatorfs_create_ulong(sb, dir, "enabled", &gator_events_ccn504_enabled[i]);
136                 if (i != CCNT) {
137                         gatorfs_create_ulong(sb, dir, "event", &gator_events_ccn504_event[i]);
138                 }
139                 gatorfs_create_ro_ulong(sb, dir, "key", &gator_events_ccn504_key[i]);
140         }
141
142         return 0;
143 }
144
145 static void gator_events_ccn504_set_dt_config(int xp_node_id, int event_num, int value)
146 {
147         u32 dt_config;
148
149         dt_config = readl(gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG);
150         dt_config |= (value + event_num) << (4*event_num);
151         writel(dt_config, gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG);
152 }
153
154 static int gator_events_ccn504_start(void)
155 {
156         int i;
157
158         gator_events_ccn504_global_enabled = 0;
159         for (i = 0; i < CNTMAX; ++i) {
160                 if (gator_events_ccn504_enabled[i]) {
161                         gator_events_ccn504_global_enabled = 1;
162                         break;
163                 }
164         }
165
166         if (!gator_events_ccn504_global_enabled) {
167                 return 0;
168         }
169
170         memset(&gator_events_ccn504_prev, 0x80, sizeof(gator_events_ccn504_prev));
171
172         // Disable INTREQ on overflow
173         // [6] ovfl_intr_en = 0
174         // perhaps set to 1?
175         // [5] cntr_rst = 0
176         // No register paring
177         // [4:1] cntcfg = 0
178         // Enable PMU features
179         // [0] pmu_en = 1
180         writel(0x1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMCR);
181
182         // Configure the XPs
183         for (i = 0; i < NUM_XPS; ++i) {
184                 int dt_control;
185
186                 // Pass on all events
187                 writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG);
188
189                 // Enable PMU capability
190                 // [0] dt_enable = 1
191                 dt_control = readl(gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONTROL);
192                 dt_control |= 0x1;
193                 writel(dt_control, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONTROL);
194         }
195
196         // Assume no other pmu_event_sel registers are set
197
198         // cycle counter does not need to be enabled
199         for (i = 0; i < CCNT; ++i) {
200                 int pmu_event_id;
201                 int node_type;
202                 int region;
203                 u32 pmu_event_sel;
204                 u32 oly_id_whole;
205                 u32 oly_id;
206                 u32 node_id;
207
208                 if (!gator_events_ccn504_enabled[i]) {
209                         continue;
210                 }
211
212                 pmu_event_id = get_pmu_event_id(gator_events_ccn504_event[i]);
213                 node_type = get_node_type(gator_events_ccn504_event[i]);
214                 region = get_region(gator_events_ccn504_event[i]);
215
216                 // Verify the node_type
217                 oly_id_whole = readl(gator_events_ccn504_base + region*REGION_SIZE + OLY_ID);
218                 oly_id = oly_id_whole & 0x1F;
219                 node_id = (oly_id_whole >> 8) & 0x7F;
220                 if ((oly_id != node_type) ||
221                                 ((node_type == 0x16) && ((oly_id != 0x14) && (oly_id != 0x15) && (oly_id != 0x16) && (oly_id != 0x18) && (oly_id != 0x19) && (oly_id != 0x1A)))) {
222                         printk(KERN_ERR "gator: oly_id is 0x%x expected 0x%x\n", oly_id, node_type);
223                         return -1;
224                 }
225
226                 // Set the control register
227                 pmu_event_sel = readl(gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL);
228                 switch (node_type) {
229                 case 0x08: // XP
230                         pmu_event_sel |= pmu_event_id << (7*i);
231                         gator_events_ccn504_set_dt_config(node_id, i, 0x4);
232                         break;
233                 case 0x04: // HN-F
234                 case 0x16: // RN-I
235                 case 0x10: // SBAS
236                         pmu_event_sel |= pmu_event_id << (4*i);
237                         gator_events_ccn504_set_dt_config(node_id/2, i, (node_id & 1) == 0 ? 0x8 : 0xC);
238                         break;
239                 }
240                 writel(pmu_event_sel, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL);
241         }
242
243         return 0;
244 }
245
246 static void gator_events_ccn504_stop(void)
247 {
248         int i;
249
250         if (!gator_events_ccn504_global_enabled) {
251                 return;
252         }
253
254         // cycle counter does not need to be disabled
255         for (i = 0; i < CCNT; ++i) {
256                 int region;
257
258                 if (!gator_events_ccn504_enabled[i]) {
259                         continue;
260                 }
261
262                 region = get_region(gator_events_ccn504_event[i]);
263
264                 writel(0, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL);
265         }
266
267         // Clear dt_config
268         for (i = 0; i < NUM_XPS; ++i) {
269                 writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG);
270         }
271 }
272
273 static int gator_events_ccn504_read(int **buffer)
274 {
275         int i;
276         int len = 0;
277         int value;
278
279         if (!on_primary_core() || !gator_events_ccn504_global_enabled) {
280                 return 0;
281         }
282
283         // Verify the pmsr register is zero
284         while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) != 0);
285
286         // Request a PMU snapshot
287         writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_REQ);
288
289         // Wait for the snapshot
290         while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) == 0);
291
292         // Read the shadow registers
293         for (i = 0; i < CNTMAX; ++i) {
294                 if (!gator_events_ccn504_enabled[i]) {
295                         continue;
296                 }
297
298                 value = readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + (i == CCNT ? PMCCNTRSR : PMEVCNTSR0 + 8*i));
299                 if (gator_events_ccn504_prev[i] != 0x80808080) {
300                         gator_events_ccn504_buffer[len++] = gator_events_ccn504_key[i];
301                         gator_events_ccn504_buffer[len++] = value - gator_events_ccn504_prev[i];
302                 }
303                 gator_events_ccn504_prev[i] = value;
304
305                 // Are the counters registers cleared when read? Is that what the cntr_rst bit on the pmcr register does?
306         }
307
308         // Clear the PMU snapshot status
309         writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_CLR);
310
311         if (buffer)
312                 *buffer = gator_events_ccn504_buffer;
313
314         return len;
315 }
316
317 static struct gator_interface gator_events_ccn504_interface = {
318         .shutdown = gator_events_ccn504_create_shutdown,
319         .create_files = gator_events_ccn504_create_files,
320         .start = gator_events_ccn504_start,
321         .stop = gator_events_ccn504_stop,
322         .read = gator_events_ccn504_read,
323 };
324
325 int gator_events_ccn504_init(void)
326 {
327         int i;
328
329         if (ccn504_addr == 0) {
330                 return -1;
331         }
332
333         gator_events_ccn504_base = ioremap(ccn504_addr, NUM_REGIONS*REGION_SIZE);
334         if (gator_events_ccn504_base == NULL) {
335                 printk(KERN_ERR "gator: ioremap returned NULL\n");
336                 return -1;
337         }
338
339         for (i = 0; i < CNTMAX; ++i) {
340                 gator_events_ccn504_enabled[i] = 0;
341                 gator_events_ccn504_event[i] = 0;
342                 gator_events_ccn504_key[i] = gator_events_get_key();
343         }
344
345         return gator_events_install(&gator_events_ccn504_interface);
346 }