blob: 6036b23741c3b7a9b34e34dcc5bfd1b67f51deae [file] [log] [blame]
Patrick Williams2194f502022-10-16 14:26:09 -05001From a4c8c0c804b1a92373faceda9350512f2139562a Mon Sep 17 00:00:00 2001
Brad Bishopbec4ebc2022-08-03 09:55:16 -04002From: Robin Murphy <robin.murphy@arm.com>
3Date: Fri, 3 Dec 2021 11:44:55 +0000
Patrick Williams2194f502022-10-16 14:26:09 -05004Subject: [PATCH 20/40] perf/arm-cmn: Refactor DTM handling
Brad Bishopbec4ebc2022-08-03 09:55:16 -04005
6Untangle DTMs from XPs into a dedicated abstraction. This helps make
7things a little more obvious and robust, but primarily paves the way
8for further development where new IPs can grow extra DTMs per XP.
9
10Signed-off-by: Robin Murphy <robin.murphy@arm.com>
11Link: https://lore.kernel.org/r/9cca18b1b98f482df7f1aaf3d3213e7f39500423.1638530442.git.robin.murphy@arm.com
12Signed-off-by: Will Deacon <will@kernel.org>
13
14Upstream-Status: Backport [https://lore.kernel.org/r/9cca18b1b98f482df7f1aaf3d3213e7f39500423.1638530442.git.robin.murphy@arm.com]
15Signed-off-by: Rupinderjit Singh <rupinderjit.singh@arm.com>
16---
17 drivers/perf/arm-cmn.c | 169 +++++++++++++++++++++--------------------
18 1 file changed, 87 insertions(+), 82 deletions(-)
19
20diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c
Patrick Williams2194f502022-10-16 14:26:09 -050021index da7bf779fec2..8b98ca9666d0 100644
Brad Bishopbec4ebc2022-08-03 09:55:16 -040022--- a/drivers/perf/arm-cmn.c
23+++ b/drivers/perf/arm-cmn.c
24@@ -35,14 +35,9 @@
25 #define CMN_CHILD_NODE_ADDR GENMASK(27, 0)
26 #define CMN_CHILD_NODE_EXTERNAL BIT(31)
27
28-#define CMN_ADDR_NODE_PTR GENMASK(27, 14)
29-
30-#define CMN_NODE_PTR_DEVID(ptr) (((ptr) >> 2) & 3)
31-#define CMN_NODE_PTR_PID(ptr) ((ptr) & 1)
32-#define CMN_NODE_PTR_X(ptr, bits) ((ptr) >> (6 + (bits)))
33-#define CMN_NODE_PTR_Y(ptr, bits) (((ptr) >> 6) & ((1U << (bits)) - 1))
34-
35-#define CMN_MAX_XPS (8 * 8)
36+#define CMN_MAX_DIMENSION 8
37+#define CMN_MAX_XPS (CMN_MAX_DIMENSION * CMN_MAX_DIMENSION)
38+#define CMN_MAX_DTMS CMN_MAX_XPS
39
40 /* The CFG node has one other useful purpose */
41 #define CMN_CFGM_PERIPH_ID_2 0x0010
42@@ -190,32 +185,32 @@ struct arm_cmn_node {
43 u16 id, logid;
44 enum cmn_node_type type;
45
46+ int dtm;
47 union {
48- /* Device node */
49+ /* DN/HN-F/CXHA */
50 struct {
51- int to_xp;
52- /* DN/HN-F/CXHA */
53- unsigned int occupid_val;
54- unsigned int occupid_count;
55+ u8 occupid_val;
56+ u8 occupid_count;
57 };
58 /* XP */
59- struct {
60- int dtc;
61- u32 pmu_config_low;
62- union {
63- u8 input_sel[4];
64- __le32 pmu_config_high;
65- };
66- s8 wp_event[4];
67- };
68+ int dtc;
69 };
70-
71 union {
72 u8 event[4];
73 __le32 event_sel;
74 };
75 };
76
77+struct arm_cmn_dtm {
78+ void __iomem *base;
79+ u32 pmu_config_low;
80+ union {
81+ u8 input_sel[4];
82+ __le32 pmu_config_high;
83+ };
84+ s8 wp_event[4];
85+};
86+
87 struct arm_cmn_dtc {
88 void __iomem *base;
89 int irq;
90@@ -241,6 +236,7 @@ struct arm_cmn {
91 struct arm_cmn_node *xps;
92 struct arm_cmn_node *dns;
93
94+ struct arm_cmn_dtm *dtms;
95 struct arm_cmn_dtc *dtc;
96 unsigned int num_dtcs;
97
98@@ -282,20 +278,14 @@ static struct arm_cmn_nodeid arm_cmn_nid(const struct arm_cmn *cmn, u16 id)
99 return nid;
100 }
101
102-static void arm_cmn_init_node_to_xp(const struct arm_cmn *cmn,
103- struct arm_cmn_node *dn)
104+static struct arm_cmn_node *arm_cmn_node_to_xp(const struct arm_cmn *cmn,
105+ const struct arm_cmn_node *dn)
106 {
107 struct arm_cmn_nodeid nid = arm_cmn_nid(cmn, dn->id);
108 int xp_idx = cmn->mesh_x * nid.y + nid.x;
109
110- dn->to_xp = (cmn->xps + xp_idx) - dn;
111-}
112-
113-static struct arm_cmn_node *arm_cmn_node_to_xp(struct arm_cmn_node *dn)
114-{
115- return dn->type == CMN_TYPE_XP ? dn : dn + dn->to_xp;
116+ return cmn->xps + xp_idx;
117 }
118-
119 static struct arm_cmn_node *arm_cmn_node(const struct arm_cmn *cmn,
120 enum cmn_node_type type)
121 {
122@@ -706,9 +696,9 @@ static u64 arm_cmn_read_dtm(struct arm_cmn *cmn, struct arm_cmn_hw_event *hw,
123
124 offset = snapshot ? CMN_DTM_PMEVCNTSR : CMN_DTM_PMEVCNT;
125 for_each_hw_dn(hw, dn, i) {
126- struct arm_cmn_node *xp = arm_cmn_node_to_xp(dn);
127+ struct arm_cmn_dtm *dtm = &cmn->dtms[dn->dtm];
128 int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
129- u64 reg = readq_relaxed(xp->pmu_base + offset);
130+ u64 reg = readq_relaxed(dtm->base + offset);
131 u16 dtm_count = reg >> (dtm_idx * 16);
132
133 count += dtm_count;
134@@ -835,9 +825,9 @@ static void arm_cmn_event_stop(struct perf_event *event, int flags)
135 }
136
137 struct arm_cmn_val {
138- u8 dtm_count[CMN_MAX_XPS];
139- u8 occupid[CMN_MAX_XPS];
140- u8 wp[CMN_MAX_XPS][4];
141+ u8 dtm_count[CMN_MAX_DTMS];
142+ u8 occupid[CMN_MAX_DTMS];
143+ u8 wp[CMN_MAX_DTMS][4];
144 int dtc_count;
145 bool cycles;
146 };
147@@ -866,16 +856,16 @@ static void arm_cmn_val_add_event(struct arm_cmn_val *val, struct perf_event *ev
148 occupid = 0;
149
150 for_each_hw_dn(hw, dn, i) {
151- int wp_idx, xp = arm_cmn_node_to_xp(dn)->logid;
152+ int wp_idx, dtm = dn->dtm;
153
154- val->dtm_count[xp]++;
155- val->occupid[xp] = occupid;
156+ val->dtm_count[dtm]++;
157+ val->occupid[dtm] = occupid;
158
159 if (type != CMN_TYPE_WP)
160 continue;
161
162 wp_idx = arm_cmn_wp_idx(event);
163- val->wp[xp][wp_idx] = CMN_EVENT_WP_COMBINE(event) + 1;
164+ val->wp[dtm][wp_idx] = CMN_EVENT_WP_COMBINE(event) + 1;
165 }
166 }
167
168@@ -914,22 +904,22 @@ static int arm_cmn_validate_group(struct perf_event *event)
169 occupid = 0;
170
171 for_each_hw_dn(hw, dn, i) {
172- int wp_idx, wp_cmb, xp = arm_cmn_node_to_xp(dn)->logid;
173+ int wp_idx, wp_cmb, dtm = dn->dtm;
174
175- if (val.dtm_count[xp] == CMN_DTM_NUM_COUNTERS)
176+ if (val.dtm_count[dtm] == CMN_DTM_NUM_COUNTERS)
177 return -EINVAL;
178
179- if (occupid && val.occupid[xp] && occupid != val.occupid[xp])
180+ if (occupid && val.occupid[dtm] && occupid != val.occupid[dtm])
181 return -EINVAL;
182
183 if (type != CMN_TYPE_WP)
184 continue;
185
186 wp_idx = arm_cmn_wp_idx(event);
187- if (val.wp[xp][wp_idx])
188+ if (val.wp[dtm][wp_idx])
189 return -EINVAL;
190
191- wp_cmb = val.wp[xp][wp_idx ^ 1];
192+ wp_cmb = val.wp[dtm][wp_idx ^ 1];
193 if (wp_cmb && wp_cmb != CMN_EVENT_WP_COMBINE(event) + 1)
194 return -EINVAL;
195 }
196@@ -1010,17 +1000,17 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
197 enum cmn_node_type type = CMN_EVENT_TYPE(event);
198
199 while (i--) {
200- struct arm_cmn_node *xp = arm_cmn_node_to_xp(hw->dn + i);
201+ struct arm_cmn_dtm *dtm = &cmn->dtms[hw->dn[i].dtm];
202 unsigned int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
203
204 if (type == CMN_TYPE_WP)
205- hw->dn[i].wp_event[arm_cmn_wp_idx(event)] = -1;
206+ dtm->wp_event[arm_cmn_wp_idx(event)] = -1;
207
208 if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event)))
209 hw->dn[i].occupid_count--;
210
211- xp->pmu_config_low &= ~CMN__PMEVCNT_PAIRED(dtm_idx);
212- writel_relaxed(xp->pmu_config_low, xp->pmu_base + CMN_DTM_PMU_CONFIG);
213+ dtm->pmu_config_low &= ~CMN__PMEVCNT_PAIRED(dtm_idx);
214+ writel_relaxed(dtm->pmu_config_low, dtm->base + CMN_DTM_PMU_CONFIG);
215 }
216 memset(hw->dtm_idx, 0, sizeof(hw->dtm_idx));
217
218@@ -1062,12 +1052,12 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
219
220 /* ...then the local counters to feed it. */
221 for_each_hw_dn(hw, dn, i) {
222- struct arm_cmn_node *xp = arm_cmn_node_to_xp(dn);
223+ struct arm_cmn_dtm *dtm = &cmn->dtms[dn->dtm];
224 unsigned int dtm_idx, shift;
225 u64 reg;
226
227 dtm_idx = 0;
228- while (xp->pmu_config_low & CMN__PMEVCNT_PAIRED(dtm_idx))
229+ while (dtm->pmu_config_low & CMN__PMEVCNT_PAIRED(dtm_idx))
230 if (++dtm_idx == CMN_DTM_NUM_COUNTERS)
231 goto free_dtms;
232
233@@ -1077,17 +1067,17 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
234 int tmp, wp_idx = arm_cmn_wp_idx(event);
235 u32 cfg = arm_cmn_wp_config(event);
236
237- if (dn->wp_event[wp_idx] >= 0)
238+ if (dtm->wp_event[wp_idx] >= 0)
239 goto free_dtms;
240
241- tmp = dn->wp_event[wp_idx ^ 1];
242+ tmp = dtm->wp_event[wp_idx ^ 1];
243 if (tmp >= 0 && CMN_EVENT_WP_COMBINE(event) !=
244 CMN_EVENT_WP_COMBINE(dtc->counters[tmp]))
245 goto free_dtms;
246
247 input_sel = CMN__PMEVCNT0_INPUT_SEL_WP + wp_idx;
248- dn->wp_event[wp_idx] = dtc_idx;
249- writel_relaxed(cfg, dn->pmu_base + CMN_DTM_WPn_CONFIG(wp_idx));
250+ dtm->wp_event[wp_idx] = dtc_idx;
251+ writel_relaxed(cfg, dtm->base + CMN_DTM_WPn_CONFIG(wp_idx));
252 } else {
253 struct arm_cmn_nodeid nid = arm_cmn_nid(cmn, dn->id);
254
255@@ -1095,7 +1085,7 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
256 (nid.port << 4) + (nid.dev << 2);
257
258 if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event))) {
259- int occupid = CMN_EVENT_OCCUPID(event);
260+ u8 occupid = CMN_EVENT_OCCUPID(event);
261
262 if (dn->occupid_count == 0) {
263 dn->occupid_val = occupid;
264@@ -1110,13 +1100,13 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
265
266 arm_cmn_set_index(hw->dtm_idx, i, dtm_idx);
267
268- xp->input_sel[dtm_idx] = input_sel;
269+ dtm->input_sel[dtm_idx] = input_sel;
270 shift = CMN__PMEVCNTn_GLOBAL_NUM_SHIFT(dtm_idx);
271- xp->pmu_config_low &= ~(CMN__PMEVCNT0_GLOBAL_NUM << shift);
272- xp->pmu_config_low |= FIELD_PREP(CMN__PMEVCNT0_GLOBAL_NUM, dtc_idx) << shift;
273- xp->pmu_config_low |= CMN__PMEVCNT_PAIRED(dtm_idx);
274- reg = (u64)le32_to_cpu(xp->pmu_config_high) << 32 | xp->pmu_config_low;
275- writeq_relaxed(reg, xp->pmu_base + CMN_DTM_PMU_CONFIG);
276+ dtm->pmu_config_low &= ~(CMN__PMEVCNT0_GLOBAL_NUM << shift);
277+ dtm->pmu_config_low |= FIELD_PREP(CMN__PMEVCNT0_GLOBAL_NUM, dtc_idx) << shift;
278+ dtm->pmu_config_low |= CMN__PMEVCNT_PAIRED(dtm_idx);
279+ reg = (u64)le32_to_cpu(dtm->pmu_config_high) << 32 | dtm->pmu_config_low;
280+ writeq_relaxed(reg, dtm->base + CMN_DTM_PMU_CONFIG);
281 }
282
283 /* Go go go! */
284@@ -1276,23 +1266,22 @@ static int arm_cmn_init_irqs(struct arm_cmn *cmn)
285 return 0;
286 }
287
288-static void arm_cmn_init_dtm(struct arm_cmn_node *xp)
289+static void arm_cmn_init_dtm(struct arm_cmn_dtm *dtm, struct arm_cmn_node *xp)
290 {
291 int i;
292
293+ dtm->base = xp->pmu_base;
294+ dtm->pmu_config_low = CMN_DTM_PMU_CONFIG_PMU_EN;
295 for (i = 0; i < 4; i++) {
296- xp->wp_event[i] = -1;
297- writeq_relaxed(0, xp->pmu_base + CMN_DTM_WPn_MASK(i));
298- writeq_relaxed(~0ULL, xp->pmu_base + CMN_DTM_WPn_VAL(i));
299+ dtm->wp_event[i] = -1;
300+ writeq_relaxed(0, dtm->base + CMN_DTM_WPn_MASK(i));
301+ writeq_relaxed(~0ULL, dtm->base + CMN_DTM_WPn_VAL(i));
302 }
303- xp->pmu_config_low = CMN_DTM_PMU_CONFIG_PMU_EN;
304- xp->dtc = -1;
305 }
306
307 static int arm_cmn_init_dtc(struct arm_cmn *cmn, struct arm_cmn_node *dn, int idx)
308 {
309 struct arm_cmn_dtc *dtc = cmn->dtc + idx;
310- struct arm_cmn_node *xp;
311
312 dtc->base = dn->pmu_base - CMN_PMU_OFFSET;
313 dtc->irq = platform_get_irq(to_platform_device(cmn->dev), idx);
314@@ -1303,10 +1292,6 @@ static int arm_cmn_init_dtc(struct arm_cmn *cmn, struct arm_cmn_node *dn, int id
315 writel_relaxed(0x1ff, dtc->base + CMN_DT_PMOVSR_CLR);
316 writel_relaxed(CMN_DT_PMCR_OVFL_INTR_EN, dtc->base + CMN_DT_PMCR);
317
318- /* We do at least know that a DTC's XP must be in that DTC's domain */
319- xp = arm_cmn_node_to_xp(dn);
320- xp->dtc = idx;
321-
322 return 0;
323 }
324
325@@ -1323,7 +1308,7 @@ static int arm_cmn_node_cmp(const void *a, const void *b)
326
327 static int arm_cmn_init_dtcs(struct arm_cmn *cmn)
328 {
329- struct arm_cmn_node *dn;
330+ struct arm_cmn_node *dn, *xp;
331 int dtc_idx = 0;
332
333 cmn->dtc = devm_kcalloc(cmn->dev, cmn->num_dtcs, sizeof(cmn->dtc[0]), GFP_KERNEL);
334@@ -1335,13 +1320,24 @@ static int arm_cmn_init_dtcs(struct arm_cmn *cmn)
335 cmn->xps = arm_cmn_node(cmn, CMN_TYPE_XP);
336
337 for (dn = cmn->dns; dn->type; dn++) {
338- if (dn->type != CMN_TYPE_XP)
339- arm_cmn_init_node_to_xp(cmn, dn);
340- else if (cmn->num_dtcs == 1)
341- dn->dtc = 0;
342+ if (dn->type == CMN_TYPE_XP) {
343+ if (dn->dtc < 0 && cmn->num_dtcs == 1)
344+ dn->dtc = 0;
345+ continue;
346+ }
347
348- if (dn->type == CMN_TYPE_DTC)
349- arm_cmn_init_dtc(cmn, dn, dtc_idx++);
350+ xp = arm_cmn_node_to_xp(cmn, dn);
351+ dn->dtm = xp->dtm;
352+
353+ if (dn->type == CMN_TYPE_DTC) {
354+ int err;
355+ /* We do at least know that a DTC's XP must be in that DTC's domain */
356+ if (xp->dtc < 0)
357+ xp->dtc = dtc_idx;
358+ err = arm_cmn_init_dtc(cmn, dn, dtc_idx++);
359+ if (err)
360+ return err;
361+ }
362
363 /* To the PMU, RN-Ds don't add anything over RN-Is, so smoosh them together */
364 if (dn->type == CMN_TYPE_RND)
365@@ -1380,6 +1376,7 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
366 {
367 void __iomem *cfg_region;
368 struct arm_cmn_node cfg, *dn;
369+ struct arm_cmn_dtm *dtm;
370 u16 child_count, child_poff;
371 u32 xp_offset[CMN_MAX_XPS];
372 u64 reg;
373@@ -1416,14 +1413,18 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
374 if (!dn)
375 return -ENOMEM;
376
377+ dtm = devm_kcalloc(cmn->dev, cmn->num_xps, sizeof(*dtm), GFP_KERNEL);
378+ if (!dtm)
379+ return -ENOMEM;
380+
381 /* Pass 2: now we can actually populate the nodes */
382 cmn->dns = dn;
383+ cmn->dtms = dtm;
384 for (i = 0; i < cmn->num_xps; i++) {
385 void __iomem *xp_region = cmn->base + xp_offset[i];
386 struct arm_cmn_node *xp = dn++;
387
388 arm_cmn_init_node_info(cmn, xp_offset[i], xp);
389- arm_cmn_init_dtm(xp);
390 /*
391 * Thanks to the order in which XP logical IDs seem to be
392 * assigned, we can handily infer the mesh X dimension by
393@@ -1433,6 +1434,10 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
394 if (xp->id == (1 << 3))
395 cmn->mesh_x = xp->logid;
396
397+ xp->dtc = -1;
398+ xp->dtm = dtm - cmn->dtms;
399+ arm_cmn_init_dtm(dtm++, xp);
400+
401 reg = readq_relaxed(xp_region + CMN_CHILD_INFO);
402 child_count = FIELD_GET(CMN_CI_CHILD_COUNT, reg);
403 child_poff = FIELD_GET(CMN_CI_CHILD_PTR_OFFSET, reg);
404--
Patrick Williams2194f502022-10-16 14:26:09 -05004052.34.1
Brad Bishopbec4ebc2022-08-03 09:55:16 -0400406