blob: 8c3c7c151679d886478fa7f55815b781e4927f17 [file] [log] [blame]
Patrick Williams2194f502022-10-16 14:26:09 -05001From 821c1c36ba8883a8709bbbcdf4ebc716e69da991 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:45:00 +0000
Patrick Williams2194f502022-10-16 14:26:09 -05004Subject: [PATCH 25/40] perf/arm-cmn: Support new IP features
Brad Bishopbec4ebc2022-08-03 09:55:16 -04005
6The second generation of CMN IPs add new node types and significantly
7expand the configuration space with options for extra device ports on
8edge XPs, either plumbed into the regular DTM or with extra dedicated
9DTMs to monitor them, plus larger (and smaller) mesh sizes. Add basic
10support for pulling this new information out of the hardware, piping
11it around as necessary, and handling (most of) the new choices.
12
13Signed-off-by: Robin Murphy <robin.murphy@arm.com>
14Link: https://lore.kernel.org/r/e58b495bcc7deec3882be4bac910ed0bf6979674.1638530442.git.robin.murphy@arm.com
15Signed-off-by: Will Deacon <will@kernel.org>
Patrick Williams2390b1b2022-11-03 13:47:49 -050016
17Upstream-Status: Backport [https://lore.kernel.org/r/e58b495bcc7deec3882be4bac910ed0bf6979674.1638530442.git.robin.murphy@arm.com]
Brad Bishopbec4ebc2022-08-03 09:55:16 -040018Signed-off-by: Rupinderjit Singh <rupinderjit.singh@arm.com>
Patrick Williams2194f502022-10-16 14:26:09 -050019Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
Brad Bishopbec4ebc2022-08-03 09:55:16 -040020---
Patrick Williams2194f502022-10-16 14:26:09 -050021 drivers/perf/arm-cmn.c | 218 ++++++++++++++++++++++++++++++++---------
22 1 file changed, 171 insertions(+), 47 deletions(-)
Brad Bishopbec4ebc2022-08-03 09:55:16 -040023
24diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c
Patrick Williams2194f502022-10-16 14:26:09 -050025index ce94f923a607..0a3f33a83c01 100644
Brad Bishopbec4ebc2022-08-03 09:55:16 -040026--- a/drivers/perf/arm-cmn.c
27+++ b/drivers/perf/arm-cmn.c
28@@ -24,7 +24,10 @@
29 #define CMN_NI_LOGICAL_ID GENMASK_ULL(47, 32)
30
31 #define CMN_NODEID_DEVID(reg) ((reg) & 3)
32+#define CMN_NODEID_EXT_DEVID(reg) ((reg) & 1)
33 #define CMN_NODEID_PID(reg) (((reg) >> 2) & 1)
34+#define CMN_NODEID_EXT_PID(reg) (((reg) >> 1) & 3)
35+#define CMN_NODEID_1x1_PID(reg) (((reg) >> 2) & 7)
36 #define CMN_NODEID_X(reg, bits) ((reg) >> (3 + (bits)))
37 #define CMN_NODEID_Y(reg, bits) (((reg) >> 3) & ((1U << (bits)) - 1))
38
39@@ -37,13 +40,26 @@
40
41 #define CMN_MAX_DIMENSION 8
42 #define CMN_MAX_XPS (CMN_MAX_DIMENSION * CMN_MAX_DIMENSION)
43-#define CMN_MAX_DTMS CMN_MAX_XPS
44+#define CMN_MAX_DTMS (CMN_MAX_XPS + (CMN_MAX_DIMENSION - 1) * 4)
45
46-/* The CFG node has one other useful purpose */
47+/* The CFG node has various info besides the discovery tree */
48 #define CMN_CFGM_PERIPH_ID_2 0x0010
49 #define CMN_CFGM_PID2_REVISION GENMASK(7, 4)
50
51-/* PMU registers occupy the 3rd 4KB page of each node's 16KB space */
52+#define CMN_CFGM_INFO_GLOBAL 0x900
53+#define CMN_INFO_MULTIPLE_DTM_EN BIT_ULL(63)
54+#define CMN_INFO_RSP_VC_NUM GENMASK_ULL(53, 52)
55+#define CMN_INFO_DAT_VC_NUM GENMASK_ULL(51, 50)
56+
57+/* XPs also have some local topology info which has uses too */
58+#define CMN_MXP__CONNECT_INFO_P0 0x0008
59+#define CMN_MXP__CONNECT_INFO_P1 0x0010
60+#define CMN_MXP__CONNECT_INFO_P2 0x0028
61+#define CMN_MXP__CONNECT_INFO_P3 0x0030
62+#define CMN_MXP__CONNECT_INFO_P4 0x0038
63+#define CMN_MXP__CONNECT_INFO_P5 0x0040
64+
65+/* PMU registers occupy the 3rd 4KB page of each node's region */
66 #define CMN_PMU_OFFSET 0x2000
67
68 /* For most nodes, this is all there is */
69@@ -53,6 +69,7 @@
70 /* DTMs live in the PMU space of XP registers */
71 #define CMN_DTM_WPn(n) (0x1A0 + (n) * 0x18)
72 #define CMN_DTM_WPn_CONFIG(n) (CMN_DTM_WPn(n) + 0x00)
73+#define CMN_DTM_WPn_CONFIG_WP_DEV_SEL2 GENMASK_ULL(18,17)
74 #define CMN_DTM_WPn_CONFIG_WP_COMBINE BIT(6)
75 #define CMN_DTM_WPn_CONFIG_WP_EXCLUSIVE BIT(5)
76 #define CMN_DTM_WPn_CONFIG_WP_GRP BIT(4)
77@@ -77,7 +94,11 @@
78
79 #define CMN_DTM_PMEVCNTSR 0x240
80
81+#define CMN_DTM_UNIT_INFO 0x0910
82+
83 #define CMN_DTM_NUM_COUNTERS 4
84+/* Want more local counters? Why not replicate the whole DTM! Ugh... */
85+#define CMN_DTM_OFFSET(n) ((n) * 0x200)
86
87 /* The DTC node is where the magic happens */
88 #define CMN_DT_DTC_CTL 0x0a00
89@@ -131,10 +152,10 @@
90 #define CMN_EVENT_NODEID(event) FIELD_GET(CMN_CONFIG_NODEID, (event)->attr.config)
91
92 #define CMN_CONFIG_WP_COMBINE GENMASK_ULL(27, 24)
93-#define CMN_CONFIG_WP_DEV_SEL BIT_ULL(48)
94-#define CMN_CONFIG_WP_CHN_SEL GENMASK_ULL(50, 49)
95-#define CMN_CONFIG_WP_GRP BIT_ULL(52)
96-#define CMN_CONFIG_WP_EXCLUSIVE BIT_ULL(53)
97+#define CMN_CONFIG_WP_DEV_SEL GENMASK_ULL(50, 48)
98+#define CMN_CONFIG_WP_CHN_SEL GENMASK_ULL(55, 51)
99+#define CMN_CONFIG_WP_GRP BIT_ULL(56)
100+#define CMN_CONFIG_WP_EXCLUSIVE BIT_ULL(57)
101 #define CMN_CONFIG1_WP_VAL GENMASK_ULL(63, 0)
102 #define CMN_CONFIG2_WP_MASK GENMASK_ULL(63, 0)
103
104@@ -176,9 +197,12 @@ enum cmn_node_type {
105 CMN_TYPE_HNF,
106 CMN_TYPE_XP,
107 CMN_TYPE_SBSX,
108- CMN_TYPE_RNI = 0xa,
109+ CMN_TYPE_MPAM_S,
110+ CMN_TYPE_MPAM_NS,
111+ CMN_TYPE_RNI,
112 CMN_TYPE_RND = 0xd,
113 CMN_TYPE_RNSAM = 0xf,
114+ CMN_TYPE_MTSX,
115 CMN_TYPE_CXRA = 0x100,
116 CMN_TYPE_CXHA = 0x101,
117 CMN_TYPE_CXLA = 0x102,
118@@ -233,6 +257,7 @@ struct arm_cmn_dtc {
119 struct arm_cmn {
120 struct device *dev;
121 void __iomem *base;
122+ unsigned int state;
123
124 enum cmn_revision rev;
125 enum cmn_model model;
126@@ -240,6 +265,13 @@ struct arm_cmn {
127 u8 mesh_y;
128 u16 num_xps;
129 u16 num_dns;
130+ bool multi_dtm;
131+ u8 ports_used;
132+ struct {
133+ unsigned int rsp_vc_num : 2;
134+ unsigned int dat_vc_num : 2;
135+ };
136+
137 struct arm_cmn_node *xps;
138 struct arm_cmn_node *dns;
139
140@@ -250,7 +282,6 @@ struct arm_cmn {
141 int cpu;
142 struct hlist_node cpuhp_node;
143
144- unsigned int state;
145 struct pmu pmu;
146 };
147
148@@ -275,13 +306,25 @@ static int arm_cmn_xyidbits(const struct arm_cmn *cmn)
149 static struct arm_cmn_nodeid arm_cmn_nid(const struct arm_cmn *cmn, u16 id)
150 {
151 struct arm_cmn_nodeid nid;
152- int bits = arm_cmn_xyidbits(cmn);
153
154- nid.x = CMN_NODEID_X(id, bits);
155- nid.y = CMN_NODEID_Y(id, bits);
156- nid.port = CMN_NODEID_PID(id);
157- nid.dev = CMN_NODEID_DEVID(id);
158+ if (cmn->num_xps == 1) {
159+ nid.x = 0;
160+ nid.y = 0;
161+ nid.port = CMN_NODEID_1x1_PID(id);
162+ nid.dev = CMN_NODEID_DEVID(id);
163+ } else {
164+ int bits = arm_cmn_xyidbits(cmn);
165
166+ nid.x = CMN_NODEID_X(id, bits);
167+ nid.y = CMN_NODEID_Y(id, bits);
168+ if (cmn->ports_used & 0xc) {
169+ nid.port = CMN_NODEID_EXT_PID(id);
170+ nid.dev = CMN_NODEID_EXT_DEVID(id);
171+ } else {
172+ nid.port = CMN_NODEID_PID(id);
173+ nid.dev = CMN_NODEID_DEVID(id);
174+ }
175+ }
176 return nid;
177 }
178
179@@ -310,6 +353,7 @@ struct arm_cmn_hw_event {
180 unsigned int dtc_idx;
181 u8 dtcs_used;
182 u8 num_dns;
183+ u8 dtm_offset;
184 };
185
186 #define for_each_hw_dn(hw, dn, i) \
187@@ -354,7 +398,8 @@ struct arm_cmn_format_attr {
188 .occupid = _occupid, \
189 }})[0].attr.attr)
190
191-static bool arm_cmn_is_occup_event(enum cmn_node_type type, unsigned int id)
192+static bool arm_cmn_is_occup_event(enum cmn_model model,
193+ enum cmn_node_type type, unsigned int id)
194 {
195 return (type == CMN_TYPE_DVM && id == 0x05) ||
196 (type == CMN_TYPE_HNF && id == 0x0f);
Patrick Williams2194f502022-10-16 14:26:09 -0500197@@ -375,7 +420,7 @@ static ssize_t arm_cmn_event_show(struct device *dev,
198 "type=0x%x,eventid=0x%x,wp_dev_sel=?,wp_chn_sel=?,wp_grp=?,wp_val=?,wp_mask=?\n",
199 eattr->type, eattr->eventid);
Brad Bishopbec4ebc2022-08-03 09:55:16 -0400200
201- if (arm_cmn_is_occup_event(eattr->type, eattr->eventid))
Brad Bishopbec4ebc2022-08-03 09:55:16 -0400202+ if (arm_cmn_is_occup_event(eattr->model, eattr->type, eattr->eventid))
Patrick Williams2194f502022-10-16 14:26:09 -0500203 return sysfs_emit(buf, "type=0x%x,eventid=0x%x,occupid=0x%x\n",
204 eattr->type, eattr->eventid, eattr->occupid);
Brad Bishopbec4ebc2022-08-03 09:55:16 -0400205
Brad Bishopbec4ebc2022-08-03 09:55:16 -0400206@@ -390,25 +435,36 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
207 struct device *dev = kobj_to_dev(kobj);
208 struct arm_cmn *cmn = to_cmn(dev_get_drvdata(dev));
209 struct arm_cmn_event_attr *eattr;
210- enum cmn_node_type type;
211
212 eattr = container_of(attr, typeof(*eattr), attr.attr);
213- type = eattr->type;
214
215 if (!(eattr->model & cmn->model))
216 return 0;
217
218- /* Watchpoints aren't nodes */
219- if (type == CMN_TYPE_WP)
220- type = CMN_TYPE_XP;
221+ /* Watchpoints aren't nodes, so avoid confusion */
222+ if (eattr->type == CMN_TYPE_WP)
223+ return attr->mode;
224+
225+ /* Hide XP events for unused interfaces/channels */
226+ if (eattr->type == CMN_TYPE_XP) {
227+ unsigned int intf = (eattr->eventid >> 2) & 7;
228+ unsigned int chan = eattr->eventid >> 5;
229+
230+ if ((intf & 4) && !(cmn->ports_used & BIT(intf & 3)))
231+ return 0;
232+
233+ if ((chan == 5 && cmn->rsp_vc_num < 2) ||
234+ (chan == 6 && cmn->dat_vc_num < 2))
235+ return 0;
236+ }
237
238 /* Revision-specific differences */
239 if (cmn->model == CMN600 && cmn->rev < CMN600_R1P2) {
240- if (type == CMN_TYPE_HNF && eattr->eventid == 0x1b)
241+ if (eattr->type == CMN_TYPE_HNF && eattr->eventid == 0x1b)
242 return 0;
243 }
244
245- if (!arm_cmn_node(cmn, type))
246+ if (!arm_cmn_node(cmn, eattr->type))
247 return 0;
248
249 return attr->mode;
250@@ -669,7 +725,8 @@ static u32 arm_cmn_wp_config(struct perf_event *event)
251 config = FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_DEV_SEL, dev) |
252 FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_CHN_SEL, chn) |
253 FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_GRP, grp) |
254- FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_EXCLUSIVE, exc);
255+ FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_EXCLUSIVE, exc) |
256+ FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_DEV_SEL2, dev >> 1);
257 if (combine && !grp)
258 config |= CMN_DTM_WPn_CONFIG_WP_COMBINE;
259
260@@ -712,7 +769,7 @@ static u64 arm_cmn_read_dtm(struct arm_cmn *cmn, struct arm_cmn_hw_event *hw,
261 offset = snapshot ? CMN_DTM_PMEVCNTSR : CMN_DTM_PMEVCNT;
262 for_each_hw_dn(hw, dn, i) {
263 if (dtm != &cmn->dtms[dn->dtm]) {
264- dtm = &cmn->dtms[dn->dtm];
265+ dtm = &cmn->dtms[dn->dtm] + hw->dtm_offset;
266 reg = readq_relaxed(dtm->base + offset);
267 }
268 dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
269@@ -800,8 +857,10 @@ static void arm_cmn_event_start(struct perf_event *event, int flags)
270 u64 mask = CMN_EVENT_WP_MASK(event);
271
272 for_each_hw_dn(hw, dn, i) {
273- writeq_relaxed(val, dn->pmu_base + CMN_DTM_WPn_VAL(wp_idx));
274- writeq_relaxed(mask, dn->pmu_base + CMN_DTM_WPn_MASK(wp_idx));
275+ void __iomem *base = dn->pmu_base + CMN_DTM_OFFSET(hw->dtm_offset);
276+
277+ writeq_relaxed(val, base + CMN_DTM_WPn_VAL(wp_idx));
278+ writeq_relaxed(mask, base + CMN_DTM_WPn_MASK(wp_idx));
279 }
280 } else for_each_hw_dn(hw, dn, i) {
281 int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
282@@ -826,8 +885,10 @@ static void arm_cmn_event_stop(struct perf_event *event, int flags)
283 int wp_idx = arm_cmn_wp_idx(event);
284
285 for_each_hw_dn(hw, dn, i) {
286- writeq_relaxed(0, dn->pmu_base + CMN_DTM_WPn_MASK(wp_idx));
287- writeq_relaxed(~0ULL, dn->pmu_base + CMN_DTM_WPn_VAL(wp_idx));
288+ void __iomem *base = dn->pmu_base + CMN_DTM_OFFSET(hw->dtm_offset);
289+
290+ writeq_relaxed(0, base + CMN_DTM_WPn_MASK(wp_idx));
291+ writeq_relaxed(~0ULL, base + CMN_DTM_WPn_VAL(wp_idx));
292 }
293 } else for_each_hw_dn(hw, dn, i) {
294 int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
295@@ -847,7 +908,8 @@ struct arm_cmn_val {
296 bool cycles;
297 };
298
299-static void arm_cmn_val_add_event(struct arm_cmn_val *val, struct perf_event *event)
300+static void arm_cmn_val_add_event(struct arm_cmn *cmn, struct arm_cmn_val *val,
301+ struct perf_event *event)
302 {
303 struct arm_cmn_hw_event *hw = to_cmn_hw(event);
304 struct arm_cmn_node *dn;
305@@ -865,7 +927,7 @@ static void arm_cmn_val_add_event(struct arm_cmn_val *val, struct perf_event *ev
306 }
307
308 val->dtc_count++;
309- if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event)))
310+ if (arm_cmn_is_occup_event(cmn->model, type, CMN_EVENT_EVENTID(event)))
311 occupid = CMN_EVENT_OCCUPID(event) + 1;
312 else
313 occupid = 0;
314@@ -884,7 +946,7 @@ static void arm_cmn_val_add_event(struct arm_cmn_val *val, struct perf_event *ev
315 }
316 }
317
318-static int arm_cmn_validate_group(struct perf_event *event)
319+static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event)
320 {
321 struct arm_cmn_hw_event *hw = to_cmn_hw(event);
322 struct arm_cmn_node *dn;
323@@ -904,9 +966,9 @@ static int arm_cmn_validate_group(struct perf_event *event)
324 if (!val)
325 return -ENOMEM;
326
327- arm_cmn_val_add_event(val, leader);
328+ arm_cmn_val_add_event(cmn, val, leader);
329 for_each_sibling_event(sibling, leader)
330- arm_cmn_val_add_event(val, sibling);
331+ arm_cmn_val_add_event(cmn, val, sibling);
332
333 type = CMN_EVENT_TYPE(event);
334 if (type == CMN_TYPE_DTC) {
335@@ -917,7 +979,7 @@ static int arm_cmn_validate_group(struct perf_event *event)
336 if (val->dtc_count == CMN_DT_NUM_COUNTERS)
337 goto done;
338
339- if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event)))
340+ if (arm_cmn_is_occup_event(cmn->model, type, CMN_EVENT_EVENTID(event)))
341 occupid = CMN_EVENT_OCCUPID(event) + 1;
342 else
343 occupid = 0;
344@@ -980,6 +1042,9 @@ static int arm_cmn_event_init(struct perf_event *event)
345 eventid = CMN_EVENT_EVENTID(event);
346 if (eventid != CMN_WP_UP && eventid != CMN_WP_DOWN)
347 return -EINVAL;
348+ /* ...but the DTM may depend on which port we're watching */
349+ if (cmn->multi_dtm)
350+ hw->dtm_offset = CMN_EVENT_WP_DEV_SEL(event) / 2;
351 }
352
353 bynodeid = CMN_EVENT_BYNODEID(event);
354@@ -1007,7 +1072,7 @@ static int arm_cmn_event_init(struct perf_event *event)
355 return -EINVAL;
356 }
357
358- return arm_cmn_validate_group(event);
359+ return arm_cmn_validate_group(cmn, event);
360 }
361
362 static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
363@@ -1017,13 +1082,13 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
364 enum cmn_node_type type = CMN_EVENT_TYPE(event);
365
366 while (i--) {
367- struct arm_cmn_dtm *dtm = &cmn->dtms[hw->dn[i].dtm];
368+ struct arm_cmn_dtm *dtm = &cmn->dtms[hw->dn[i].dtm] + hw->dtm_offset;
369 unsigned int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
370
371 if (type == CMN_TYPE_WP)
372 dtm->wp_event[arm_cmn_wp_idx(event)] = -1;
373
374- if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event)))
375+ if (arm_cmn_is_occup_event(cmn->model, type, CMN_EVENT_EVENTID(event)))
376 hw->dn[i].occupid_count--;
377
378 dtm->pmu_config_low &= ~CMN__PMEVCNT_PAIRED(dtm_idx);
379@@ -1069,7 +1134,7 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
380
381 /* ...then the local counters to feed it. */
382 for_each_hw_dn(hw, dn, i) {
383- struct arm_cmn_dtm *dtm = &cmn->dtms[dn->dtm];
384+ struct arm_cmn_dtm *dtm = &cmn->dtms[dn->dtm] + hw->dtm_offset;
385 unsigned int dtm_idx, shift;
386 u64 reg;
387
388@@ -1098,10 +1163,13 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
389 } else {
390 struct arm_cmn_nodeid nid = arm_cmn_nid(cmn, dn->id);
391
392+ if (cmn->multi_dtm)
393+ nid.port %= 2;
394+
395 input_sel = CMN__PMEVCNT0_INPUT_SEL_DEV + dtm_idx +
396 (nid.port << 4) + (nid.dev << 2);
397
398- if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event))) {
399+ if (arm_cmn_is_occup_event(cmn->model, type, CMN_EVENT_EVENTID(event))) {
400 u8 occupid = CMN_EVENT_OCCUPID(event);
401
402 if (dn->occupid_count == 0) {
403@@ -1283,11 +1351,11 @@ static int arm_cmn_init_irqs(struct arm_cmn *cmn)
404 return 0;
405 }
406
407-static void arm_cmn_init_dtm(struct arm_cmn_dtm *dtm, struct arm_cmn_node *xp)
408+static void arm_cmn_init_dtm(struct arm_cmn_dtm *dtm, struct arm_cmn_node *xp, int idx)
409 {
410 int i;
411
412- dtm->base = xp->pmu_base;
413+ dtm->base = xp->pmu_base + CMN_DTM_OFFSET(idx);
414 dtm->pmu_config_low = CMN_DTM_PMU_CONFIG_PMU_EN;
415 for (i = 0; i < 4; i++) {
416 dtm->wp_event[i] = -1;
417@@ -1345,6 +1413,8 @@ static int arm_cmn_init_dtcs(struct arm_cmn *cmn)
418
419 xp = arm_cmn_node_to_xp(cmn, dn);
420 dn->dtm = xp->dtm;
421+ if (cmn->multi_dtm)
422+ dn->dtm += arm_cmn_nid(cmn, dn->id).port / 2;
423
424 if (dn->type == CMN_TYPE_DTC) {
425 int err;
426@@ -1408,6 +1478,11 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
427 reg = readl_relaxed(cfg_region + CMN_CFGM_PERIPH_ID_2);
428 cmn->rev = FIELD_GET(CMN_CFGM_PID2_REVISION, reg);
429
430+ reg = readq_relaxed(cfg_region + CMN_CFGM_INFO_GLOBAL);
431+ cmn->multi_dtm = reg & CMN_INFO_MULTIPLE_DTM_EN;
432+ cmn->rsp_vc_num = FIELD_GET(CMN_INFO_RSP_VC_NUM, reg);
433+ cmn->dat_vc_num = FIELD_GET(CMN_INFO_DAT_VC_NUM, reg);
434+
435 reg = readq_relaxed(cfg_region + CMN_CHILD_INFO);
436 child_count = FIELD_GET(CMN_CI_CHILD_COUNT, reg);
437 child_poff = FIELD_GET(CMN_CI_CHILD_PTR_OFFSET, reg);
438@@ -1429,7 +1504,11 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
439 if (!dn)
440 return -ENOMEM;
441
442- dtm = devm_kcalloc(cmn->dev, cmn->num_xps, sizeof(*dtm), GFP_KERNEL);
443+ /* Initial safe upper bound on DTMs for any possible mesh layout */
444+ i = cmn->num_xps;
445+ if (cmn->multi_dtm)
446+ i += cmn->num_xps + 1;
447+ dtm = devm_kcalloc(cmn->dev, i, sizeof(*dtm), GFP_KERNEL);
448 if (!dtm)
449 return -ENOMEM;
450
451@@ -1439,6 +1518,7 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
452 for (i = 0; i < cmn->num_xps; i++) {
453 void __iomem *xp_region = cmn->base + xp_offset[i];
454 struct arm_cmn_node *xp = dn++;
455+ unsigned int xp_ports = 0;
456
457 arm_cmn_init_node_info(cmn, xp_offset[i], xp);
458 /*
459@@ -1450,9 +1530,39 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
460 if (xp->id == (1 << 3))
461 cmn->mesh_x = xp->logid;
462
463- xp->dtc = 0xf;
464+ if (cmn->model == CMN600)
465+ xp->dtc = 0xf;
466+ else
467+ xp->dtc = 1 << readl_relaxed(xp_region + CMN_DTM_UNIT_INFO);
468+
469 xp->dtm = dtm - cmn->dtms;
470- arm_cmn_init_dtm(dtm++, xp);
471+ arm_cmn_init_dtm(dtm++, xp, 0);
472+ /*
473+ * Keeping track of connected ports will let us filter out
474+ * unnecessary XP events easily. We can also reliably infer the
475+ * "extra device ports" configuration for the node ID format
476+ * from this, since in that case we will see at least one XP
477+ * with port 2 connected, for the HN-D.
478+ */
479+ if (readq_relaxed(xp_region + CMN_MXP__CONNECT_INFO_P0))
480+ xp_ports |= BIT(0);
481+ if (readq_relaxed(xp_region + CMN_MXP__CONNECT_INFO_P1))
482+ xp_ports |= BIT(1);
483+ if (readq_relaxed(xp_region + CMN_MXP__CONNECT_INFO_P2))
484+ xp_ports |= BIT(2);
485+ if (readq_relaxed(xp_region + CMN_MXP__CONNECT_INFO_P3))
486+ xp_ports |= BIT(3);
487+ if (readq_relaxed(xp_region + CMN_MXP__CONNECT_INFO_P4))
488+ xp_ports |= BIT(4);
489+ if (readq_relaxed(xp_region + CMN_MXP__CONNECT_INFO_P5))
490+ xp_ports |= BIT(5);
491+
492+ if (cmn->multi_dtm && (xp_ports & 0xc))
493+ arm_cmn_init_dtm(dtm++, xp, 1);
494+ if (cmn->multi_dtm && (xp_ports & 0x30))
495+ arm_cmn_init_dtm(dtm++, xp, 2);
496+
497+ cmn->ports_used |= xp_ports;
498
499 reg = readq_relaxed(xp_region + CMN_CHILD_INFO);
500 child_count = FIELD_GET(CMN_CI_CHILD_COUNT, reg);
501@@ -1488,11 +1598,14 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
502 case CMN_TYPE_SBSX:
503 case CMN_TYPE_RNI:
504 case CMN_TYPE_RND:
505+ case CMN_TYPE_MTSX:
506 case CMN_TYPE_CXRA:
507 case CMN_TYPE_CXHA:
508 dn++;
509 break;
510 /* Nothing to see here */
511+ case CMN_TYPE_MPAM_S:
512+ case CMN_TYPE_MPAM_NS:
513 case CMN_TYPE_RNSAM:
514 case CMN_TYPE_CXLA:
515 break;
516@@ -1512,6 +1625,11 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
517 if (dn)
518 cmn->dns = dn;
519
520+ sz = (void *)dtm - (void *)cmn->dtms;
521+ dtm = devm_krealloc(cmn->dev, cmn->dtms, sz, GFP_KERNEL);
522+ if (dtm)
523+ cmn->dtms = dtm;
524+
525 /*
526 * If mesh_x wasn't set during discovery then we never saw
527 * an XP at (0,1), thus we must have an Nx1 configuration.
528@@ -1520,9 +1638,15 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
529 cmn->mesh_x = cmn->num_xps;
530 cmn->mesh_y = cmn->num_xps / cmn->mesh_x;
531
532+ /* 1x1 config plays havoc with XP event encodings */
533+ if (cmn->num_xps == 1)
534+ dev_warn(cmn->dev, "1x1 config not fully supported, translate XP events manually\n");
535+
536 dev_dbg(cmn->dev, "model %d, periph_id_2 revision %d\n", cmn->model, cmn->rev);
537- dev_dbg(cmn->dev, "mesh %dx%d, ID width %d\n",
538- cmn->mesh_x, cmn->mesh_y, arm_cmn_xyidbits(cmn));
539+ reg = cmn->ports_used;
540+ dev_dbg(cmn->dev, "mesh %dx%d, ID width %d, ports %6pbl%s\n",
541+ cmn->mesh_x, cmn->mesh_y, arm_cmn_xyidbits(cmn), &reg,
542+ cmn->multi_dtm ? ", multi-DTM" : "");
543
544 return 0;
545 }
546--
Patrick Williams2194f502022-10-16 14:26:09 -05005472.34.1
Brad Bishopbec4ebc2022-08-03 09:55:16 -0400548