summaryrefslogtreecommitdiff
path: root/meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.15/tc/0025-perf-arm-cmn-Support-new-IP-features.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.15/tc/0025-perf-arm-cmn-Support-new-IP-features.patch')
-rw-r--r--meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.15/tc/0025-perf-arm-cmn-Support-new-IP-features.patch547
1 files changed, 547 insertions, 0 deletions
diff --git a/meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.15/tc/0025-perf-arm-cmn-Support-new-IP-features.patch b/meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.15/tc/0025-perf-arm-cmn-Support-new-IP-features.patch
new file mode 100644
index 0000000000..fc9e5839b3
--- /dev/null
+++ b/meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.15/tc/0025-perf-arm-cmn-Support-new-IP-features.patch
@@ -0,0 +1,547 @@
+From 821c1c36ba8883a8709bbbcdf4ebc716e69da991 Mon Sep 17 00:00:00 2001
+From: Robin Murphy <robin.murphy@arm.com>
+Date: Fri, 3 Dec 2021 11:45:00 +0000
+Subject: [PATCH 25/40] perf/arm-cmn: Support new IP features
+
+The second generation of CMN IPs add new node types and significantly
+expand the configuration space with options for extra device ports on
+edge XPs, either plumbed into the regular DTM or with extra dedicated
+DTMs to monitor them, plus larger (and smaller) mesh sizes. Add basic
+support for pulling this new information out of the hardware, piping
+it around as necessary, and handling (most of) the new choices.
+
+Signed-off-by: Robin Murphy <robin.murphy@arm.com>
+Link: https://lore.kernel.org/r/e58b495bcc7deec3882be4bac910ed0bf6979674.1638530442.git.robin.murphy@arm.com
+Signed-off-by: Will Deacon <will@kernel.org>
+Upstream-Status = Backport [https://lore.kernel.org/r/e58b495bcc7deec3882be4bac910ed0bf6979674.1638530442.git.robin.murphy@arm.com]
+Signed-off-by: Rupinderjit Singh <rupinderjit.singh@arm.com>
+Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
+---
+ drivers/perf/arm-cmn.c | 218 ++++++++++++++++++++++++++++++++---------
+ 1 file changed, 171 insertions(+), 47 deletions(-)
+
+diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c
+index ce94f923a607..0a3f33a83c01 100644
+--- a/drivers/perf/arm-cmn.c
++++ b/drivers/perf/arm-cmn.c
+@@ -24,7 +24,10 @@
+ #define CMN_NI_LOGICAL_ID GENMASK_ULL(47, 32)
+
+ #define CMN_NODEID_DEVID(reg) ((reg) & 3)
++#define CMN_NODEID_EXT_DEVID(reg) ((reg) & 1)
+ #define CMN_NODEID_PID(reg) (((reg) >> 2) & 1)
++#define CMN_NODEID_EXT_PID(reg) (((reg) >> 1) & 3)
++#define CMN_NODEID_1x1_PID(reg) (((reg) >> 2) & 7)
+ #define CMN_NODEID_X(reg, bits) ((reg) >> (3 + (bits)))
+ #define CMN_NODEID_Y(reg, bits) (((reg) >> 3) & ((1U << (bits)) - 1))
+
+@@ -37,13 +40,26 @@
+
+ #define CMN_MAX_DIMENSION 8
+ #define CMN_MAX_XPS (CMN_MAX_DIMENSION * CMN_MAX_DIMENSION)
+-#define CMN_MAX_DTMS CMN_MAX_XPS
++#define CMN_MAX_DTMS (CMN_MAX_XPS + (CMN_MAX_DIMENSION - 1) * 4)
+
+-/* The CFG node has one other useful purpose */
++/* The CFG node has various info besides the discovery tree */
+ #define CMN_CFGM_PERIPH_ID_2 0x0010
+ #define CMN_CFGM_PID2_REVISION GENMASK(7, 4)
+
+-/* PMU registers occupy the 3rd 4KB page of each node's 16KB space */
++#define CMN_CFGM_INFO_GLOBAL 0x900
++#define CMN_INFO_MULTIPLE_DTM_EN BIT_ULL(63)
++#define CMN_INFO_RSP_VC_NUM GENMASK_ULL(53, 52)
++#define CMN_INFO_DAT_VC_NUM GENMASK_ULL(51, 50)
++
++/* XPs also have some local topology info which has uses too */
++#define CMN_MXP__CONNECT_INFO_P0 0x0008
++#define CMN_MXP__CONNECT_INFO_P1 0x0010
++#define CMN_MXP__CONNECT_INFO_P2 0x0028
++#define CMN_MXP__CONNECT_INFO_P3 0x0030
++#define CMN_MXP__CONNECT_INFO_P4 0x0038
++#define CMN_MXP__CONNECT_INFO_P5 0x0040
++
++/* PMU registers occupy the 3rd 4KB page of each node's region */
+ #define CMN_PMU_OFFSET 0x2000
+
+ /* For most nodes, this is all there is */
+@@ -53,6 +69,7 @@
+ /* DTMs live in the PMU space of XP registers */
+ #define CMN_DTM_WPn(n) (0x1A0 + (n) * 0x18)
+ #define CMN_DTM_WPn_CONFIG(n) (CMN_DTM_WPn(n) + 0x00)
++#define CMN_DTM_WPn_CONFIG_WP_DEV_SEL2 GENMASK_ULL(18,17)
+ #define CMN_DTM_WPn_CONFIG_WP_COMBINE BIT(6)
+ #define CMN_DTM_WPn_CONFIG_WP_EXCLUSIVE BIT(5)
+ #define CMN_DTM_WPn_CONFIG_WP_GRP BIT(4)
+@@ -77,7 +94,11 @@
+
+ #define CMN_DTM_PMEVCNTSR 0x240
+
++#define CMN_DTM_UNIT_INFO 0x0910
++
+ #define CMN_DTM_NUM_COUNTERS 4
++/* Want more local counters? Why not replicate the whole DTM! Ugh... */
++#define CMN_DTM_OFFSET(n) ((n) * 0x200)
+
+ /* The DTC node is where the magic happens */
+ #define CMN_DT_DTC_CTL 0x0a00
+@@ -131,10 +152,10 @@
+ #define CMN_EVENT_NODEID(event) FIELD_GET(CMN_CONFIG_NODEID, (event)->attr.config)
+
+ #define CMN_CONFIG_WP_COMBINE GENMASK_ULL(27, 24)
+-#define CMN_CONFIG_WP_DEV_SEL BIT_ULL(48)
+-#define CMN_CONFIG_WP_CHN_SEL GENMASK_ULL(50, 49)
+-#define CMN_CONFIG_WP_GRP BIT_ULL(52)
+-#define CMN_CONFIG_WP_EXCLUSIVE BIT_ULL(53)
++#define CMN_CONFIG_WP_DEV_SEL GENMASK_ULL(50, 48)
++#define CMN_CONFIG_WP_CHN_SEL GENMASK_ULL(55, 51)
++#define CMN_CONFIG_WP_GRP BIT_ULL(56)
++#define CMN_CONFIG_WP_EXCLUSIVE BIT_ULL(57)
+ #define CMN_CONFIG1_WP_VAL GENMASK_ULL(63, 0)
+ #define CMN_CONFIG2_WP_MASK GENMASK_ULL(63, 0)
+
+@@ -176,9 +197,12 @@ enum cmn_node_type {
+ CMN_TYPE_HNF,
+ CMN_TYPE_XP,
+ CMN_TYPE_SBSX,
+- CMN_TYPE_RNI = 0xa,
++ CMN_TYPE_MPAM_S,
++ CMN_TYPE_MPAM_NS,
++ CMN_TYPE_RNI,
+ CMN_TYPE_RND = 0xd,
+ CMN_TYPE_RNSAM = 0xf,
++ CMN_TYPE_MTSX,
+ CMN_TYPE_CXRA = 0x100,
+ CMN_TYPE_CXHA = 0x101,
+ CMN_TYPE_CXLA = 0x102,
+@@ -233,6 +257,7 @@ struct arm_cmn_dtc {
+ struct arm_cmn {
+ struct device *dev;
+ void __iomem *base;
++ unsigned int state;
+
+ enum cmn_revision rev;
+ enum cmn_model model;
+@@ -240,6 +265,13 @@ struct arm_cmn {
+ u8 mesh_y;
+ u16 num_xps;
+ u16 num_dns;
++ bool multi_dtm;
++ u8 ports_used;
++ struct {
++ unsigned int rsp_vc_num : 2;
++ unsigned int dat_vc_num : 2;
++ };
++
+ struct arm_cmn_node *xps;
+ struct arm_cmn_node *dns;
+
+@@ -250,7 +282,6 @@ struct arm_cmn {
+ int cpu;
+ struct hlist_node cpuhp_node;
+
+- unsigned int state;
+ struct pmu pmu;
+ };
+
+@@ -275,13 +306,25 @@ static int arm_cmn_xyidbits(const struct arm_cmn *cmn)
+ static struct arm_cmn_nodeid arm_cmn_nid(const struct arm_cmn *cmn, u16 id)
+ {
+ struct arm_cmn_nodeid nid;
+- int bits = arm_cmn_xyidbits(cmn);
+
+- nid.x = CMN_NODEID_X(id, bits);
+- nid.y = CMN_NODEID_Y(id, bits);
+- nid.port = CMN_NODEID_PID(id);
+- nid.dev = CMN_NODEID_DEVID(id);
++ if (cmn->num_xps == 1) {
++ nid.x = 0;
++ nid.y = 0;
++ nid.port = CMN_NODEID_1x1_PID(id);
++ nid.dev = CMN_NODEID_DEVID(id);
++ } else {
++ int bits = arm_cmn_xyidbits(cmn);
+
++ nid.x = CMN_NODEID_X(id, bits);
++ nid.y = CMN_NODEID_Y(id, bits);
++ if (cmn->ports_used & 0xc) {
++ nid.port = CMN_NODEID_EXT_PID(id);
++ nid.dev = CMN_NODEID_EXT_DEVID(id);
++ } else {
++ nid.port = CMN_NODEID_PID(id);
++ nid.dev = CMN_NODEID_DEVID(id);
++ }
++ }
+ return nid;
+ }
+
+@@ -310,6 +353,7 @@ struct arm_cmn_hw_event {
+ unsigned int dtc_idx;
+ u8 dtcs_used;
+ u8 num_dns;
++ u8 dtm_offset;
+ };
+
+ #define for_each_hw_dn(hw, dn, i) \
+@@ -354,7 +398,8 @@ struct arm_cmn_format_attr {
+ .occupid = _occupid, \
+ }})[0].attr.attr)
+
+-static bool arm_cmn_is_occup_event(enum cmn_node_type type, unsigned int id)
++static bool arm_cmn_is_occup_event(enum cmn_model model,
++ enum cmn_node_type type, unsigned int id)
+ {
+ return (type == CMN_TYPE_DVM && id == 0x05) ||
+ (type == CMN_TYPE_HNF && id == 0x0f);
+@@ -375,7 +420,7 @@ static ssize_t arm_cmn_event_show(struct device *dev,
+ "type=0x%x,eventid=0x%x,wp_dev_sel=?,wp_chn_sel=?,wp_grp=?,wp_val=?,wp_mask=?\n",
+ eattr->type, eattr->eventid);
+
+- if (arm_cmn_is_occup_event(eattr->type, eattr->eventid))
++ if (arm_cmn_is_occup_event(eattr->model, eattr->type, eattr->eventid))
+ return sysfs_emit(buf, "type=0x%x,eventid=0x%x,occupid=0x%x\n",
+ eattr->type, eattr->eventid, eattr->occupid);
+
+@@ -390,25 +435,36 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
+ struct device *dev = kobj_to_dev(kobj);
+ struct arm_cmn *cmn = to_cmn(dev_get_drvdata(dev));
+ struct arm_cmn_event_attr *eattr;
+- enum cmn_node_type type;
+
+ eattr = container_of(attr, typeof(*eattr), attr.attr);
+- type = eattr->type;
+
+ if (!(eattr->model & cmn->model))
+ return 0;
+
+- /* Watchpoints aren't nodes */
+- if (type == CMN_TYPE_WP)
+- type = CMN_TYPE_XP;
++ /* Watchpoints aren't nodes, so avoid confusion */
++ if (eattr->type == CMN_TYPE_WP)
++ return attr->mode;
++
++ /* Hide XP events for unused interfaces/channels */
++ if (eattr->type == CMN_TYPE_XP) {
++ unsigned int intf = (eattr->eventid >> 2) & 7;
++ unsigned int chan = eattr->eventid >> 5;
++
++ if ((intf & 4) && !(cmn->ports_used & BIT(intf & 3)))
++ return 0;
++
++ if ((chan == 5 && cmn->rsp_vc_num < 2) ||
++ (chan == 6 && cmn->dat_vc_num < 2))
++ return 0;
++ }
+
+ /* Revision-specific differences */
+ if (cmn->model == CMN600 && cmn->rev < CMN600_R1P2) {
+- if (type == CMN_TYPE_HNF && eattr->eventid == 0x1b)
++ if (eattr->type == CMN_TYPE_HNF && eattr->eventid == 0x1b)
+ return 0;
+ }
+
+- if (!arm_cmn_node(cmn, type))
++ if (!arm_cmn_node(cmn, eattr->type))
+ return 0;
+
+ return attr->mode;
+@@ -669,7 +725,8 @@ static u32 arm_cmn_wp_config(struct perf_event *event)
+ config = FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_DEV_SEL, dev) |
+ FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_CHN_SEL, chn) |
+ FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_GRP, grp) |
+- FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_EXCLUSIVE, exc);
++ FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_EXCLUSIVE, exc) |
++ FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_DEV_SEL2, dev >> 1);
+ if (combine && !grp)
+ config |= CMN_DTM_WPn_CONFIG_WP_COMBINE;
+
+@@ -712,7 +769,7 @@ static u64 arm_cmn_read_dtm(struct arm_cmn *cmn, struct arm_cmn_hw_event *hw,
+ offset = snapshot ? CMN_DTM_PMEVCNTSR : CMN_DTM_PMEVCNT;
+ for_each_hw_dn(hw, dn, i) {
+ if (dtm != &cmn->dtms[dn->dtm]) {
+- dtm = &cmn->dtms[dn->dtm];
++ dtm = &cmn->dtms[dn->dtm] + hw->dtm_offset;
+ reg = readq_relaxed(dtm->base + offset);
+ }
+ dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
+@@ -800,8 +857,10 @@ static void arm_cmn_event_start(struct perf_event *event, int flags)
+ u64 mask = CMN_EVENT_WP_MASK(event);
+
+ for_each_hw_dn(hw, dn, i) {
+- writeq_relaxed(val, dn->pmu_base + CMN_DTM_WPn_VAL(wp_idx));
+- writeq_relaxed(mask, dn->pmu_base + CMN_DTM_WPn_MASK(wp_idx));
++ void __iomem *base = dn->pmu_base + CMN_DTM_OFFSET(hw->dtm_offset);
++
++ writeq_relaxed(val, base + CMN_DTM_WPn_VAL(wp_idx));
++ writeq_relaxed(mask, base + CMN_DTM_WPn_MASK(wp_idx));
+ }
+ } else for_each_hw_dn(hw, dn, i) {
+ int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
+@@ -826,8 +885,10 @@ static void arm_cmn_event_stop(struct perf_event *event, int flags)
+ int wp_idx = arm_cmn_wp_idx(event);
+
+ for_each_hw_dn(hw, dn, i) {
+- writeq_relaxed(0, dn->pmu_base + CMN_DTM_WPn_MASK(wp_idx));
+- writeq_relaxed(~0ULL, dn->pmu_base + CMN_DTM_WPn_VAL(wp_idx));
++ void __iomem *base = dn->pmu_base + CMN_DTM_OFFSET(hw->dtm_offset);
++
++ writeq_relaxed(0, base + CMN_DTM_WPn_MASK(wp_idx));
++ writeq_relaxed(~0ULL, base + CMN_DTM_WPn_VAL(wp_idx));
+ }
+ } else for_each_hw_dn(hw, dn, i) {
+ int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
+@@ -847,7 +908,8 @@ struct arm_cmn_val {
+ bool cycles;
+ };
+
+-static void arm_cmn_val_add_event(struct arm_cmn_val *val, struct perf_event *event)
++static void arm_cmn_val_add_event(struct arm_cmn *cmn, struct arm_cmn_val *val,
++ struct perf_event *event)
+ {
+ struct arm_cmn_hw_event *hw = to_cmn_hw(event);
+ struct arm_cmn_node *dn;
+@@ -865,7 +927,7 @@ static void arm_cmn_val_add_event(struct arm_cmn_val *val, struct perf_event *ev
+ }
+
+ val->dtc_count++;
+- if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event)))
++ if (arm_cmn_is_occup_event(cmn->model, type, CMN_EVENT_EVENTID(event)))
+ occupid = CMN_EVENT_OCCUPID(event) + 1;
+ else
+ occupid = 0;
+@@ -884,7 +946,7 @@ static void arm_cmn_val_add_event(struct arm_cmn_val *val, struct perf_event *ev
+ }
+ }
+
+-static int arm_cmn_validate_group(struct perf_event *event)
++static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event)
+ {
+ struct arm_cmn_hw_event *hw = to_cmn_hw(event);
+ struct arm_cmn_node *dn;
+@@ -904,9 +966,9 @@ static int arm_cmn_validate_group(struct perf_event *event)
+ if (!val)
+ return -ENOMEM;
+
+- arm_cmn_val_add_event(val, leader);
++ arm_cmn_val_add_event(cmn, val, leader);
+ for_each_sibling_event(sibling, leader)
+- arm_cmn_val_add_event(val, sibling);
++ arm_cmn_val_add_event(cmn, val, sibling);
+
+ type = CMN_EVENT_TYPE(event);
+ if (type == CMN_TYPE_DTC) {
+@@ -917,7 +979,7 @@ static int arm_cmn_validate_group(struct perf_event *event)
+ if (val->dtc_count == CMN_DT_NUM_COUNTERS)
+ goto done;
+
+- if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event)))
++ if (arm_cmn_is_occup_event(cmn->model, type, CMN_EVENT_EVENTID(event)))
+ occupid = CMN_EVENT_OCCUPID(event) + 1;
+ else
+ occupid = 0;
+@@ -980,6 +1042,9 @@ static int arm_cmn_event_init(struct perf_event *event)
+ eventid = CMN_EVENT_EVENTID(event);
+ if (eventid != CMN_WP_UP && eventid != CMN_WP_DOWN)
+ return -EINVAL;
++ /* ...but the DTM may depend on which port we're watching */
++ if (cmn->multi_dtm)
++ hw->dtm_offset = CMN_EVENT_WP_DEV_SEL(event) / 2;
+ }
+
+ bynodeid = CMN_EVENT_BYNODEID(event);
+@@ -1007,7 +1072,7 @@ static int arm_cmn_event_init(struct perf_event *event)
+ return -EINVAL;
+ }
+
+- return arm_cmn_validate_group(event);
++ return arm_cmn_validate_group(cmn, event);
+ }
+
+ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
+@@ -1017,13 +1082,13 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
+ enum cmn_node_type type = CMN_EVENT_TYPE(event);
+
+ while (i--) {
+- struct arm_cmn_dtm *dtm = &cmn->dtms[hw->dn[i].dtm];
++ struct arm_cmn_dtm *dtm = &cmn->dtms[hw->dn[i].dtm] + hw->dtm_offset;
+ unsigned int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
+
+ if (type == CMN_TYPE_WP)
+ dtm->wp_event[arm_cmn_wp_idx(event)] = -1;
+
+- if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event)))
++ if (arm_cmn_is_occup_event(cmn->model, type, CMN_EVENT_EVENTID(event)))
+ hw->dn[i].occupid_count--;
+
+ dtm->pmu_config_low &= ~CMN__PMEVCNT_PAIRED(dtm_idx);
+@@ -1069,7 +1134,7 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
+
+ /* ...then the local counters to feed it. */
+ for_each_hw_dn(hw, dn, i) {
+- struct arm_cmn_dtm *dtm = &cmn->dtms[dn->dtm];
++ struct arm_cmn_dtm *dtm = &cmn->dtms[dn->dtm] + hw->dtm_offset;
+ unsigned int dtm_idx, shift;
+ u64 reg;
+
+@@ -1098,10 +1163,13 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
+ } else {
+ struct arm_cmn_nodeid nid = arm_cmn_nid(cmn, dn->id);
+
++ if (cmn->multi_dtm)
++ nid.port %= 2;
++
+ input_sel = CMN__PMEVCNT0_INPUT_SEL_DEV + dtm_idx +
+ (nid.port << 4) + (nid.dev << 2);
+
+- if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event))) {
++ if (arm_cmn_is_occup_event(cmn->model, type, CMN_EVENT_EVENTID(event))) {
+ u8 occupid = CMN_EVENT_OCCUPID(event);
+
+ if (dn->occupid_count == 0) {
+@@ -1283,11 +1351,11 @@ static int arm_cmn_init_irqs(struct arm_cmn *cmn)
+ return 0;
+ }
+
+-static void arm_cmn_init_dtm(struct arm_cmn_dtm *dtm, struct arm_cmn_node *xp)
++static void arm_cmn_init_dtm(struct arm_cmn_dtm *dtm, struct arm_cmn_node *xp, int idx)
+ {
+ int i;
+
+- dtm->base = xp->pmu_base;
++ dtm->base = xp->pmu_base + CMN_DTM_OFFSET(idx);
+ dtm->pmu_config_low = CMN_DTM_PMU_CONFIG_PMU_EN;
+ for (i = 0; i < 4; i++) {
+ dtm->wp_event[i] = -1;
+@@ -1345,6 +1413,8 @@ static int arm_cmn_init_dtcs(struct arm_cmn *cmn)
+
+ xp = arm_cmn_node_to_xp(cmn, dn);
+ dn->dtm = xp->dtm;
++ if (cmn->multi_dtm)
++ dn->dtm += arm_cmn_nid(cmn, dn->id).port / 2;
+
+ if (dn->type == CMN_TYPE_DTC) {
+ int err;
+@@ -1408,6 +1478,11 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
+ reg = readl_relaxed(cfg_region + CMN_CFGM_PERIPH_ID_2);
+ cmn->rev = FIELD_GET(CMN_CFGM_PID2_REVISION, reg);
+
++ reg = readq_relaxed(cfg_region + CMN_CFGM_INFO_GLOBAL);
++ cmn->multi_dtm = reg & CMN_INFO_MULTIPLE_DTM_EN;
++ cmn->rsp_vc_num = FIELD_GET(CMN_INFO_RSP_VC_NUM, reg);
++ cmn->dat_vc_num = FIELD_GET(CMN_INFO_DAT_VC_NUM, reg);
++
+ reg = readq_relaxed(cfg_region + CMN_CHILD_INFO);
+ child_count = FIELD_GET(CMN_CI_CHILD_COUNT, reg);
+ child_poff = FIELD_GET(CMN_CI_CHILD_PTR_OFFSET, reg);
+@@ -1429,7 +1504,11 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
+ if (!dn)
+ return -ENOMEM;
+
+- dtm = devm_kcalloc(cmn->dev, cmn->num_xps, sizeof(*dtm), GFP_KERNEL);
++ /* Initial safe upper bound on DTMs for any possible mesh layout */
++ i = cmn->num_xps;
++ if (cmn->multi_dtm)
++ i += cmn->num_xps + 1;
++ dtm = devm_kcalloc(cmn->dev, i, sizeof(*dtm), GFP_KERNEL);
+ if (!dtm)
+ return -ENOMEM;
+
+@@ -1439,6 +1518,7 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
+ for (i = 0; i < cmn->num_xps; i++) {
+ void __iomem *xp_region = cmn->base + xp_offset[i];
+ struct arm_cmn_node *xp = dn++;
++ unsigned int xp_ports = 0;
+
+ arm_cmn_init_node_info(cmn, xp_offset[i], xp);
+ /*
+@@ -1450,9 +1530,39 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
+ if (xp->id == (1 << 3))
+ cmn->mesh_x = xp->logid;
+
+- xp->dtc = 0xf;
++ if (cmn->model == CMN600)
++ xp->dtc = 0xf;
++ else
++ xp->dtc = 1 << readl_relaxed(xp_region + CMN_DTM_UNIT_INFO);
++
+ xp->dtm = dtm - cmn->dtms;
+- arm_cmn_init_dtm(dtm++, xp);
++ arm_cmn_init_dtm(dtm++, xp, 0);
++ /*
++ * Keeping track of connected ports will let us filter out
++ * unnecessary XP events easily. We can also reliably infer the
++ * "extra device ports" configuration for the node ID format
++ * from this, since in that case we will see at least one XP
++ * with port 2 connected, for the HN-D.
++ */
++ if (readq_relaxed(xp_region + CMN_MXP__CONNECT_INFO_P0))
++ xp_ports |= BIT(0);
++ if (readq_relaxed(xp_region + CMN_MXP__CONNECT_INFO_P1))
++ xp_ports |= BIT(1);
++ if (readq_relaxed(xp_region + CMN_MXP__CONNECT_INFO_P2))
++ xp_ports |= BIT(2);
++ if (readq_relaxed(xp_region + CMN_MXP__CONNECT_INFO_P3))
++ xp_ports |= BIT(3);
++ if (readq_relaxed(xp_region + CMN_MXP__CONNECT_INFO_P4))
++ xp_ports |= BIT(4);
++ if (readq_relaxed(xp_region + CMN_MXP__CONNECT_INFO_P5))
++ xp_ports |= BIT(5);
++
++ if (cmn->multi_dtm && (xp_ports & 0xc))
++ arm_cmn_init_dtm(dtm++, xp, 1);
++ if (cmn->multi_dtm && (xp_ports & 0x30))
++ arm_cmn_init_dtm(dtm++, xp, 2);
++
++ cmn->ports_used |= xp_ports;
+
+ reg = readq_relaxed(xp_region + CMN_CHILD_INFO);
+ child_count = FIELD_GET(CMN_CI_CHILD_COUNT, reg);
+@@ -1488,11 +1598,14 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
+ case CMN_TYPE_SBSX:
+ case CMN_TYPE_RNI:
+ case CMN_TYPE_RND:
++ case CMN_TYPE_MTSX:
+ case CMN_TYPE_CXRA:
+ case CMN_TYPE_CXHA:
+ dn++;
+ break;
+ /* Nothing to see here */
++ case CMN_TYPE_MPAM_S:
++ case CMN_TYPE_MPAM_NS:
+ case CMN_TYPE_RNSAM:
+ case CMN_TYPE_CXLA:
+ break;
+@@ -1512,6 +1625,11 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
+ if (dn)
+ cmn->dns = dn;
+
++ sz = (void *)dtm - (void *)cmn->dtms;
++ dtm = devm_krealloc(cmn->dev, cmn->dtms, sz, GFP_KERNEL);
++ if (dtm)
++ cmn->dtms = dtm;
++
+ /*
+ * If mesh_x wasn't set during discovery then we never saw
+ * an XP at (0,1), thus we must have an Nx1 configuration.
+@@ -1520,9 +1638,15 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
+ cmn->mesh_x = cmn->num_xps;
+ cmn->mesh_y = cmn->num_xps / cmn->mesh_x;
+
++ /* 1x1 config plays havoc with XP event encodings */
++ if (cmn->num_xps == 1)
++ dev_warn(cmn->dev, "1x1 config not fully supported, translate XP events manually\n");
++
+ dev_dbg(cmn->dev, "model %d, periph_id_2 revision %d\n", cmn->model, cmn->rev);
+- dev_dbg(cmn->dev, "mesh %dx%d, ID width %d\n",
+- cmn->mesh_x, cmn->mesh_y, arm_cmn_xyidbits(cmn));
++ reg = cmn->ports_used;
++ dev_dbg(cmn->dev, "mesh %dx%d, ID width %d, ports %6pbl%s\n",
++ cmn->mesh_x, cmn->mesh_y, arm_cmn_xyidbits(cmn), &reg,
++ cmn->multi_dtm ? ", multi-DTM" : "");
+
+ return 0;
+ }
+--
+2.34.1
+