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