| 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), ®, |
| + cmn->multi_dtm ? ", multi-DTM" : ""); |
| |
| return 0; |
| } |
| -- |
| 2.34.1 |
| |