Patrick Williams | 2194f50 | 2022-10-16 14:26:09 -0500 | [diff] [blame] | 1 | From 821c1c36ba8883a8709bbbcdf4ebc716e69da991 Mon Sep 17 00:00:00 2001 |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 2 | From: Robin Murphy <robin.murphy@arm.com> |
| 3 | Date: Fri, 3 Dec 2021 11:45:00 +0000 |
Patrick Williams | 2194f50 | 2022-10-16 14:26:09 -0500 | [diff] [blame] | 4 | Subject: [PATCH 25/40] perf/arm-cmn: Support new IP features |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 5 | |
| 6 | The second generation of CMN IPs add new node types and significantly |
| 7 | expand the configuration space with options for extra device ports on |
| 8 | edge XPs, either plumbed into the regular DTM or with extra dedicated |
| 9 | DTMs to monitor them, plus larger (and smaller) mesh sizes. Add basic |
| 10 | support for pulling this new information out of the hardware, piping |
| 11 | it around as necessary, and handling (most of) the new choices. |
| 12 | |
| 13 | Signed-off-by: Robin Murphy <robin.murphy@arm.com> |
| 14 | Link: https://lore.kernel.org/r/e58b495bcc7deec3882be4bac910ed0bf6979674.1638530442.git.robin.murphy@arm.com |
| 15 | Signed-off-by: Will Deacon <will@kernel.org> |
Patrick Williams | 2390b1b | 2022-11-03 13:47:49 -0500 | [diff] [blame^] | 16 | |
| 17 | Upstream-Status: Backport [https://lore.kernel.org/r/e58b495bcc7deec3882be4bac910ed0bf6979674.1638530442.git.robin.murphy@arm.com] |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 18 | Signed-off-by: Rupinderjit Singh <rupinderjit.singh@arm.com> |
Patrick Williams | 2194f50 | 2022-10-16 14:26:09 -0500 | [diff] [blame] | 19 | Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 20 | --- |
Patrick Williams | 2194f50 | 2022-10-16 14:26:09 -0500 | [diff] [blame] | 21 | drivers/perf/arm-cmn.c | 218 ++++++++++++++++++++++++++++++++--------- |
| 22 | 1 file changed, 171 insertions(+), 47 deletions(-) |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 23 | |
| 24 | diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c |
Patrick Williams | 2194f50 | 2022-10-16 14:26:09 -0500 | [diff] [blame] | 25 | index ce94f923a607..0a3f33a83c01 100644 |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 26 | --- 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 Williams | 2194f50 | 2022-10-16 14:26:09 -0500 | [diff] [blame] | 197 | @@ -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 Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 200 | |
| 201 | - if (arm_cmn_is_occup_event(eattr->type, eattr->eventid)) |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 202 | + if (arm_cmn_is_occup_event(eattr->model, eattr->type, eattr->eventid)) |
Patrick Williams | 2194f50 | 2022-10-16 14:26:09 -0500 | [diff] [blame] | 203 | return sysfs_emit(buf, "type=0x%x,eventid=0x%x,occupid=0x%x\n", |
| 204 | eattr->type, eattr->eventid, eattr->occupid); |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 205 | |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 206 | @@ -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), ®, |
| 542 | + cmn->multi_dtm ? ", multi-DTM" : ""); |
| 543 | |
| 544 | return 0; |
| 545 | } |
| 546 | -- |
Patrick Williams | 2194f50 | 2022-10-16 14:26:09 -0500 | [diff] [blame] | 547 | 2.34.1 |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 548 | |