diff --git a/Documentation/devicetree/bindings/perf/marvell-cn10k-ddr.yaml b/Documentation/devicetree/bindings/perf/marvell-cn10k-ddr.yaml index a18dd0a8c43a..f2f0d6b61eac 100644 --- a/Documentation/devicetree/bindings/perf/marvell-cn10k-ddr.yaml +++ b/Documentation/devicetree/bindings/perf/marvell-cn10k-ddr.yaml @@ -4,16 +4,22 @@ $id: http://devicetree.org/schemas/perf/marvell-cn10k-ddr.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Marvell CN10K DDR performance monitor +title: Marvell CN10K / CN20K DDR performance monitor + +description: + Performance Monitoring Unit (PMU) for the DDR controller on Marvell + CN10K and CN20K SoCs. The block is accessed via a dedicated MMIO region. maintainers: - Bharat Bhushan + - Geetha sowjanya properties: compatible: items: - enum: - marvell,cn10k-ddr-pmu + - marvell,cn20k-ddr-pmu reg: maxItems: 1 diff --git a/drivers/acpi/arm64/apmt.c b/drivers/acpi/arm64/apmt.c index bb010f6164e5..91fcdd289e63 100644 --- a/drivers/acpi/arm64/apmt.c +++ b/drivers/acpi/arm64/apmt.c @@ -76,10 +76,12 @@ static int __init apmt_add_platform_device(struct acpi_apmt_node *node, struct fwnode_handle *fwnode) { struct platform_device *pdev; - int ret, count; + int ret, count, uid = node->id & INT_MAX; struct resource res[DEV_MAX_RESOURCE_COUNT]; - pdev = platform_device_alloc(DEV_NAME, PLATFORM_DEVID_AUTO); + if (uid != node->id) + pr_warn("Unexpectedly large UID 0x%x, truncated to 0x%x\n", node->id, uid); + pdev = platform_device_alloc(DEV_NAME, uid); if (!pdev) return -ENOMEM; diff --git a/drivers/perf/alibaba_uncore_drw_pmu.c b/drivers/perf/alibaba_uncore_drw_pmu.c index ac49d3b2dad6..de77da83df2e 100644 --- a/drivers/perf/alibaba_uncore_drw_pmu.c +++ b/drivers/perf/alibaba_uncore_drw_pmu.c @@ -449,11 +449,8 @@ static struct ali_drw_pmu_irq *__ali_drw_pmu_init_irq(struct platform_device */ ret = devm_request_irq(&pdev->dev, irq_num, ali_drw_pmu_isr, IRQF_SHARED, dev_name(&pdev->dev), irq); - if (ret < 0) { - dev_err(&pdev->dev, - "Fail to request IRQ:%d ret:%d\n", irq_num, ret); + if (ret < 0) goto out_free; - } ret = irq_set_affinity_hint(irq_num, cpumask_of(irq->cpu)); if (ret) diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c index 6e5cc4086a9e..a03e2a8ca41e 100644 --- a/drivers/perf/arm-cmn.c +++ b/drivers/perf/arm-cmn.c @@ -29,9 +29,11 @@ #define CMN_CI_CHILD_PTR_OFFSET GENMASK_ULL(31, 16) #define CMN_CHILD_NODE_ADDR GENMASK(29, 0) +#define CMN_CHILD_NODE_ISOLATED BIT(30) #define CMN_CHILD_NODE_EXTERNAL BIT(31) -#define CMN_MAX_DIMENSION 12 +/* Some implementations use a mesh larger than the architectural max of 12 */ +#define CMN_MAX_DIMENSION 14 #define CMN_MAX_XPS (CMN_MAX_DIMENSION * CMN_MAX_DIMENSION) #define CMN_MAX_DTMS (CMN_MAX_XPS + (CMN_MAX_DIMENSION - 1) * 4) @@ -47,11 +49,16 @@ #define CMN_CFGM_INFO_GLOBAL 0x0900 #define CMN_INFO_MULTIPLE_DTM_EN BIT_ULL(63) +#define CMN_S3_R2_MULTIPLE_DTM_EN BIT_ULL(59) #define CMN_INFO_RSP_VC_NUM GENMASK_ULL(53, 52) #define CMN_INFO_DAT_VC_NUM GENMASK_ULL(51, 50) #define CMN_INFO_DEVICE_ISO_ENABLE BIT_ULL(44) #define CMN_CFGM_INFO_GLOBAL_1 0x0908 +#define CMN_S3_R2_RSP_VC_NUM GENMASK_ULL(11, 9) +#define CMN_S3_R2_DAT_VC_NUM GENMASK_ULL(8, 6) +#define CMN_S3_R2_SNP_VC_NUM GENMASK_ULL(5, 3) +#define CMN_S3_R2_REQ_VC_NUM GENMASK_ULL(2, 0) #define CMN_INFO_SNP_VC_NUM GENMASK_ULL(3, 2) #define CMN_INFO_REQ_VC_NUM GENMASK_ULL(1, 0) @@ -77,6 +84,16 @@ /* Technically this is 4 bits wide on DNs, but we only use 2 there anyway */ #define CMN__PMU_OCCUP1_ID GENMASK_ULL(34, 32) +/* But then... */ +#define CMN__PMU_EVICT_STATE_SEL GENMASK_ULL(54, 52) +#define CMN__PMU_ENHANCED_HBT_LBT_SEL GENMASK_ULL(51, 48) +#define CMN__PMU_SNP_VC_SEL GENMASK_ULL(47, 46) +#define CMN__S3_R2_CBUSY_SNTHROTTLE_SEL GENMASK_ULL(45, 42) +#define CMN__S3_R2_SN_HOME_SEL GENMASK_ULL(41, 40) +#define CMN__S3_R2_HBT_LBT_SEL GENMASK_ULL(39, 38) +#define CMN__S3_R2_CLASS_OCCUP_ID GENMASK_ULL(37, 36) +#define CMN__S3_R2_OCCUP1_ID GENMASK_ULL(35, 32) + /* Some types are designed to coexist with another device in the same node */ #define CMN_CCLA_PMU_EVENT_SEL 0x008 #define CMN_HNP_PMU_EVENT_SEL 0x008 @@ -162,15 +179,17 @@ /* Event attributes */ #define CMN_CONFIG_TYPE GENMASK_ULL(15, 0) #define CMN_CONFIG_EVENTID GENMASK_ULL(26, 16) -#define CMN_CONFIG_OCCUPID GENMASK_ULL(30, 27) +#define CMN_CONFIG_FILTER GENMASK_ULL(30, 27) #define CMN_CONFIG_BYNODEID BIT_ULL(31) #define CMN_CONFIG_NODEID GENMASK_ULL(47, 32) +#define CMN_CONFIG_FILTER2 GENMASK_ULL(51, 48) #define CMN_EVENT_TYPE(event) FIELD_GET(CMN_CONFIG_TYPE, (event)->attr.config) #define CMN_EVENT_EVENTID(event) FIELD_GET(CMN_CONFIG_EVENTID, (event)->attr.config) -#define CMN_EVENT_OCCUPID(event) FIELD_GET(CMN_CONFIG_OCCUPID, (event)->attr.config) +#define CMN_EVENT_FILTER(event) FIELD_GET(CMN_CONFIG_FILTER, (event)->attr.config) #define CMN_EVENT_BYNODEID(event) FIELD_GET(CMN_CONFIG_BYNODEID, (event)->attr.config) #define CMN_EVENT_NODEID(event) FIELD_GET(CMN_CONFIG_NODEID, (event)->attr.config) +#define CMN_EVENT_FILTER2(event) FIELD_GET(CMN_CONFIG_FILTER2, (event)->attr.config) #define CMN_CONFIG_WP_COMBINE GENMASK_ULL(30, 27) #define CMN_CONFIG_WP_DEV_SEL GENMASK_ULL(50, 48) @@ -199,12 +218,14 @@ enum cmn_model { CMN650 = 2, CI700 = 4, CMN700 = 8, - CMNS3 = 16, + CMNS3R01 = 16, + CMNS3R2 = 32, /* ...and then we can use bitmap tricks for commonality */ CMN_ANY = -1, NOT_CMN600 = -2, CMN_700ON = ~(CMN700 - 1), CMN_650ON = CMN_700ON | CMN650, + CMNS3 = CMNS3R01 | CMNS3R2, }; /* Actual part numbers and revision IDs defined by the hardware */ @@ -215,6 +236,8 @@ enum cmn_part { PART_CMN700 = 0x43c, PART_CI700 = 0x43a, PART_CMN_S3 = 0x43e, + /* Synthetic part number, overridden to PART_CMN_S3 during discovery */ + PART_GRAVITON5 = 0xa5, }; /* CMN-600 r0px shouldn't exist in silicon, thankfully */ @@ -238,6 +261,10 @@ enum cmn_revision { REV_CMNS3_R0P0 = 0, REV_CMNS3_R0P1, REV_CMNS3_R1P0, + REV_CMNS3_R2P0, + REV_CMNS3_R2P1, + REV_CMNS3_R2P2, + REV_CMNS3_R2P5, REV_CI700_R0P0 = 0, REV_CI700_R1P0, REV_CI700_R2P0, @@ -275,12 +302,15 @@ enum cmn_node_type { }; enum cmn_filter_select { - SEL_NONE = -1, - SEL_OCCUP1ID, + SEL_NONE, + SEL_OCCUP1_ID, SEL_CLASS_OCCUP_ID, SEL_CBUSY_SNTHROTTLE_SEL, SEL_HBT_LBT_SEL, SEL_SN_HOME_SEL, + SEL_SNP_VC_SEL, + SEL_ENHANCED_HBT_LBT_SEL, + SEL_EVICT_STATE_SEL, SEL_MAX }; @@ -298,7 +328,7 @@ struct arm_cmn_node { struct { u8 val : 4; u8 count : 4; - } occupid[SEL_MAX]; + } filter[SEL_MAX]; union { u8 event[4]; __le32 event_sel; @@ -421,7 +451,9 @@ static enum cmn_model arm_cmn_model(const struct arm_cmn *cmn) case PART_CI700: return CI700; case PART_CMN_S3: - return CMNS3; + if (cmn->rev >= REV_CMNS3_R2P0) + return CMNS3R2; + return CMNS3R01; default: return 0; }; @@ -596,19 +628,82 @@ static void arm_cmn_debugfs_init(struct arm_cmn *cmn, int id) static void arm_cmn_debugfs_init(struct arm_cmn *cmn, int id) {} #endif +enum cmn_filter_type { + FILT_NONE, + FILT_OCCUP1_ID, + FILT_HNF_700, + FILT_HNS, + FILT_HNS_S3R2, +}; +#define CMN_FILTER(_sel) [SEL_##_sel] = CMN__PMU_##_sel +#define CMN_FILTER_V2(_sel) [SEL_##_sel] = CMN__S3_R2_##_sel + +static const u64 arm_cmn_filters[][SEL_MAX] = { + [FILT_NONE] = {}, + /* DVM etc. */ + [FILT_OCCUP1_ID] = { + CMN_FILTER(OCCUP1_ID) + }, + /* Newer HN-F */ + [FILT_HNF_700] = { + CMN_FILTER(OCCUP1_ID), + CMN_FILTER(CLASS_OCCUP_ID), + CMN_FILTER(CBUSY_SNTHROTTLE_SEL) + }, + /* HN-S */ + [FILT_HNS] = { + CMN_FILTER(OCCUP1_ID), + CMN_FILTER(CLASS_OCCUP_ID), + CMN_FILTER(CBUSY_SNTHROTTLE_SEL), + CMN_FILTER(HBT_LBT_SEL), + CMN_FILTER(SN_HOME_SEL) + }, + /* Newer HN-S */ + [FILT_HNS_S3R2] = { + CMN_FILTER_V2(OCCUP1_ID), + CMN_FILTER_V2(CLASS_OCCUP_ID), + CMN_FILTER_V2(CBUSY_SNTHROTTLE_SEL), + CMN_FILTER_V2(HBT_LBT_SEL), + CMN_FILTER_V2(SN_HOME_SEL), + CMN_FILTER(SNP_VC_SEL), + CMN_FILTER(ENHANCED_HBT_LBT_SEL), + CMN_FILTER(EVICT_STATE_SEL) + } +}; + +static enum cmn_filter_type arm_cmn_filter(enum cmn_node_type node, + enum cmn_model model) +{ + switch (node) { + default: + return FILT_NONE; + case CMN_TYPE_DVM: + case CMN_TYPE_CXRA: + case CMN_TYPE_CXHA: + case CMN_TYPE_CCRA: + case CMN_TYPE_CCHA: + return FILT_OCCUP1_ID; + case CMN_TYPE_HNF: + if (model < CMN700) + return FILT_OCCUP1_ID; + return FILT_HNF_700; + case CMN_TYPE_HNS: + if (model < CMNS3R2) + return FILT_HNS; + return FILT_HNS_S3R2; + }; +} + struct arm_cmn_hw_event { struct arm_cmn_node *dn; - u64 dtm_idx[DIV_ROUND_UP(CMN_MAX_NODES_PER_EVENT * 2, 64)]; + union { + unsigned long *dtm_idx; + int cc_idx; + }; + unsigned long *wp_idx; s8 dtc_idx[CMN_MAX_DTCS]; u8 num_dns; u8 dtm_offset; - - /* - * WP config registers are divided to UP and DOWN events. We need to - * keep to track only one of them. - */ - DECLARE_BITMAP(wp_idx, CMN_MAX_XPS); - bool wide_sel; enum cmn_filter_select filter_sel; }; @@ -626,34 +721,57 @@ static struct arm_cmn_hw_event *to_cmn_hw(struct perf_event *event) return (struct arm_cmn_hw_event *)&event->hw; } -static void arm_cmn_set_index(u64 x[], unsigned int pos, unsigned int val) +#define BPL2 (BITS_PER_LONG / 2) + +static void arm_cmn_set_dtm_idx(struct arm_cmn_hw_event *hw, unsigned int pos, unsigned int val) { - x[pos / 32] |= (u64)val << ((pos % 32) * 2); + hw->dtm_idx[pos / BPL2] |= (unsigned long)val << ((pos % BPL2) * 2); } -static unsigned int arm_cmn_get_index(u64 x[], unsigned int pos) +static unsigned int arm_cmn_get_dtm_idx(struct arm_cmn_hw_event *hw, unsigned int pos) { - return (x[pos / 32] >> ((pos % 32) * 2)) & 3; + return (hw->dtm_idx[pos / BPL2] >> ((pos % BPL2) * 2)) & 3; } -static void arm_cmn_set_wp_idx(unsigned long *wp_idx, unsigned int pos, bool val) +static unsigned long *arm_cmn_alloc_dtm_idx(void) +{ + return bitmap_zalloc(CMN_MAX_NODES_PER_EVENT * 2, GFP_KERNEL); +} + +static void arm_cmn_set_wp_idx(struct arm_cmn_hw_event *hw, unsigned int pos, bool val) { if (val) - set_bit(pos, wp_idx); + set_bit(pos, hw->wp_idx); } -static unsigned int arm_cmn_get_wp_idx(unsigned long *wp_idx, unsigned int pos) +static unsigned int arm_cmn_get_wp_idx(struct arm_cmn_hw_event *hw, unsigned int pos) { - return test_bit(pos, wp_idx); + return test_bit(pos, hw->wp_idx); } +static unsigned long *arm_cmn_alloc_wp_idx(void) +{ + return bitmap_zalloc(CMN_MAX_XPS, GFP_KERNEL); +} + +static void arm_cmn_clear_idx(struct arm_cmn_hw_event *hw) +{ + bitmap_zero(hw->dtm_idx, CMN_MAX_NODES_PER_EVENT * 2); + if (hw->wp_idx) + bitmap_zero(hw->wp_idx, CMN_MAX_XPS); +} + +struct arm_cmn_filter_attr { + enum cmn_filter_select sel; + u8 val; +}; + struct arm_cmn_event_attr { struct device_attribute attr; enum cmn_model model; enum cmn_node_type type; - enum cmn_filter_select fsel; u16 eventid; - u8 occupid; + struct arm_cmn_filter_attr filter[2]; }; struct arm_cmn_format_attr { @@ -662,24 +780,25 @@ struct arm_cmn_format_attr { int config; }; -#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _occupid, _fsel)\ +#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _fa, _fb, _fc, _fd, ...) \ (&((struct arm_cmn_event_attr[]) {{ \ .attr = __ATTR(_name, 0444, arm_cmn_event_show, NULL), \ .model = _model, \ .type = _type, \ .eventid = _eventid, \ - .occupid = _occupid, \ - .fsel = _fsel, \ + .filter = {{_fa, _fb}, {_fc, _fd}}, \ }})[0].attr.attr) -#define CMN_EVENT_ATTR(_model, _name, _type, _eventid) \ - _CMN_EVENT_ATTR(_model, _name, _type, _eventid, 0, SEL_NONE) +#define CMN_EVENT_ATTR(_model, _name, _type, _eventid, _filter...) \ + _CMN_EVENT_ATTR(_model, _name, _type, _eventid, ##_filter, 0, 0, 0, 0) static ssize_t arm_cmn_event_show(struct device *dev, struct device_attribute *attr, char *buf) { struct arm_cmn_event_attr *eattr; + struct arm_cmn_filter_attr *filter; eattr = container_of(attr, typeof(*eattr), attr); + filter = eattr->filter; if (eattr->type == CMN_TYPE_DTC) return sysfs_emit(buf, "type=0x%x\n", eattr->type); @@ -689,9 +808,13 @@ 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 (eattr->fsel > SEL_NONE) - return sysfs_emit(buf, "type=0x%x,eventid=0x%x,occupid=0x%x\n", - eattr->type, eattr->eventid, eattr->occupid); + if (filter[1].sel) + return sysfs_emit(buf, "type=0x%x,eventid=0x%x,filter=0x%x,filter2=0x%x\n", + eattr->type, eattr->eventid, filter[0].val, filter[1].val); + + if (filter[0].sel) + return sysfs_emit(buf, "type=0x%x,eventid=0x%x,filter=0x%x\n", + eattr->type, eattr->eventid, filter[0].val); return sysfs_emit(buf, "type=0x%x,eventid=0x%x\n", eattr->type, eattr->eventid); @@ -779,8 +902,8 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj, return attr->mode; } -#define _CMN_EVENT_DVM(_model, _name, _event, _occup, _fsel) \ - _CMN_EVENT_ATTR(_model, dn_##_name, CMN_TYPE_DVM, _event, _occup, _fsel) +#define CMN_EVENT_DVM(_model, _name, _event, _filter...) \ + CMN_EVENT_ATTR(_model, dn_##_name, CMN_TYPE_DVM, _event, ##_filter) #define CMN_EVENT_DTC(_name) \ CMN_EVENT_ATTR(CMN_ANY, dtc_##_name, CMN_TYPE_DTC, 0) #define CMN_EVENT_HNF(_model, _name, _event) \ @@ -807,35 +930,38 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj, CMN_EVENT_ATTR(_model, ccha_##_name, CMN_TYPE_CCHA, _event) #define CMN_EVENT_CCLA(_name, _event) \ CMN_EVENT_ATTR(CMN_ANY, ccla_##_name, CMN_TYPE_CCLA, _event) -#define CMN_EVENT_HNS(_name, _event) \ - CMN_EVENT_ATTR(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event) +#define _CMN_EVENT_HNS(_model, _name, _event, _filter...) \ + CMN_EVENT_ATTR(_model, hns_##_name, CMN_TYPE_HNS, _event, ##_filter) -#define CMN_EVENT_DVM(_model, _name, _event) \ - _CMN_EVENT_DVM(_model, _name, _event, 0, SEL_NONE) #define CMN_EVENT_DVM_OCC(_model, _name, _event) \ - _CMN_EVENT_DVM(_model, _name##_all, _event, 0, SEL_OCCUP1ID), \ - _CMN_EVENT_DVM(_model, _name##_dvmop, _event, 1, SEL_OCCUP1ID), \ - _CMN_EVENT_DVM(_model, _name##_dvmsync, _event, 2, SEL_OCCUP1ID) + CMN_EVENT_DVM(_model, _name##_all, _event, SEL_OCCUP1_ID, 0), \ + CMN_EVENT_DVM(_model, _name##_dvmop, _event, SEL_OCCUP1_ID, 1), \ + CMN_EVENT_DVM(_model, _name##_dvmsync, _event, SEL_OCCUP1_ID, 2) #define CMN_EVENT_HN_OCC(_model, _name, _type, _event) \ - _CMN_EVENT_ATTR(_model, _name##_all, _type, _event, 0, SEL_OCCUP1ID), \ - _CMN_EVENT_ATTR(_model, _name##_read, _type, _event, 1, SEL_OCCUP1ID), \ - _CMN_EVENT_ATTR(_model, _name##_write, _type, _event, 2, SEL_OCCUP1ID), \ - _CMN_EVENT_ATTR(_model, _name##_atomic, _type, _event, 3, SEL_OCCUP1ID), \ - _CMN_EVENT_ATTR(_model, _name##_stash, _type, _event, 4, SEL_OCCUP1ID) + CMN_EVENT_ATTR(_model, _name##_all, _type, _event, SEL_OCCUP1_ID, 0), \ + CMN_EVENT_ATTR(_model, _name##_read, _type, _event, SEL_OCCUP1_ID, 1), \ + CMN_EVENT_ATTR(_model, _name##_write, _type, _event, SEL_OCCUP1_ID, 2), \ + CMN_EVENT_ATTR(_model, _name##_atomic, _type, _event, SEL_OCCUP1_ID, 3), \ + CMN_EVENT_ATTR(_model, _name##_stash, _type, _event, SEL_OCCUP1_ID, 4) #define CMN_EVENT_HN_CLS(_model, _name, _type, _event) \ - _CMN_EVENT_ATTR(_model, _name##_class0, _type, _event, 0, SEL_CLASS_OCCUP_ID), \ - _CMN_EVENT_ATTR(_model, _name##_class1, _type, _event, 1, SEL_CLASS_OCCUP_ID), \ - _CMN_EVENT_ATTR(_model, _name##_class2, _type, _event, 2, SEL_CLASS_OCCUP_ID), \ - _CMN_EVENT_ATTR(_model, _name##_class3, _type, _event, 3, SEL_CLASS_OCCUP_ID) + CMN_EVENT_ATTR(_model, _name##_class0, _type, _event, SEL_CLASS_OCCUP_ID, 0), \ + CMN_EVENT_ATTR(_model, _name##_class1, _type, _event, SEL_CLASS_OCCUP_ID, 1), \ + CMN_EVENT_ATTR(_model, _name##_class2, _type, _event, SEL_CLASS_OCCUP_ID, 2), \ + CMN_EVENT_ATTR(_model, _name##_class3, _type, _event, SEL_CLASS_OCCUP_ID, 3) #define CMN_EVENT_HN_SNT(_model, _name, _type, _event) \ - _CMN_EVENT_ATTR(_model, _name##_all, _type, _event, 0, SEL_CBUSY_SNTHROTTLE_SEL), \ - _CMN_EVENT_ATTR(_model, _name##_group0_read, _type, _event, 1, SEL_CBUSY_SNTHROTTLE_SEL), \ - _CMN_EVENT_ATTR(_model, _name##_group0_write, _type, _event, 2, SEL_CBUSY_SNTHROTTLE_SEL), \ - _CMN_EVENT_ATTR(_model, _name##_group1_read, _type, _event, 3, SEL_CBUSY_SNTHROTTLE_SEL), \ - _CMN_EVENT_ATTR(_model, _name##_group1_write, _type, _event, 4, SEL_CBUSY_SNTHROTTLE_SEL), \ - _CMN_EVENT_ATTR(_model, _name##_read, _type, _event, 5, SEL_CBUSY_SNTHROTTLE_SEL), \ - _CMN_EVENT_ATTR(_model, _name##_write, _type, _event, 6, SEL_CBUSY_SNTHROTTLE_SEL) + CMN_EVENT_ATTR(_model, _name##_all, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 0), \ + CMN_EVENT_ATTR(_model, _name##_group0_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 1), \ + CMN_EVENT_ATTR(_model, _name##_group0_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 2), \ + CMN_EVENT_ATTR(_model, _name##_group1_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 3), \ + CMN_EVENT_ATTR(_model, _name##_group1_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 4), \ + CMN_EVENT_ATTR(_model, _name##_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 5), \ + CMN_EVENT_ATTR(_model, _name##_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 6), \ + CMN_EVENT_ATTR(CMNS3R2, _name##_ccg_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 9), \ + CMN_EVENT_ATTR(CMNS3R2, _name##_ccg_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 10), \ + CMN_EVENT_ATTR(CMNS3R2, _name##_lbt_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 11), \ + CMN_EVENT_ATTR(CMNS3R2, _name##_lbt_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 12), \ + CMN_EVENT_ATTR(CMNS3R2, _name##_lbt, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 13) #define CMN_EVENT_HNF_OCC(_model, _name, _event) \ CMN_EVENT_HN_OCC(_model, hnf_##_name, CMN_TYPE_HNF, _event) @@ -844,23 +970,75 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj, #define CMN_EVENT_HNF_SNT(_model, _name, _event) \ CMN_EVENT_HN_SNT(_model, hnf_##_name, CMN_TYPE_HNF, _event) -#define CMN_EVENT_HNS_OCC(_name, _event) \ - CMN_EVENT_HN_OCC(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event), \ - _CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_rxsnp, CMN_TYPE_HNS, _event, 5, SEL_OCCUP1ID), \ - _CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_lbt, CMN_TYPE_HNS, _event, 6, SEL_OCCUP1ID), \ - _CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_hbt, CMN_TYPE_HNS, _event, 7, SEL_OCCUP1ID) +#define CMN_EVENT_HNS(_name, _event) \ + _CMN_EVENT_HNS(CMN_ANY, _name, _event) +#define CMN_EVENT_HNSR0(_name, _event) \ + _CMN_EVENT_HNS(CMN700 | CMNS3R01, _name, _event) +#define _CMN_EVENT_HNS_HBT(_model, _name, _event, _sel) \ + _CMN_EVENT_HNS(_model, _name##_all, _event, _sel, 0), \ + _CMN_EVENT_HNS(_model, _name##_hbt, _event, _sel, 1), \ + _CMN_EVENT_HNS(_model, _name##_lbt, _event, _sel, 2) +#define _CMN_EVENT_HNS_HBT2(_model, _name, _event, _fsel1, f1) \ + _CMN_EVENT_HNS(_model, _name##_all, _event, _fsel1, f1, SEL_HBT_LBT_SEL, 0), \ + _CMN_EVENT_HNS(_model, _name##_hbt, _event, _fsel1, f1, SEL_HBT_LBT_SEL, 1), \ + _CMN_EVENT_HNS(_model, _name##_lbt, _event, _fsel1, f1, SEL_HBT_LBT_SEL, 2) + +#define CMN_EVENT_HNS_OCC(_model, _name, _event) \ + CMN_EVENT_HN_OCC(_model, hns_##_name, CMN_TYPE_HNS, _event), \ + _CMN_EVENT_HNS(_model, _name##_rxsnp, _event, SEL_OCCUP1_ID, 5), \ + _CMN_EVENT_HNS(_model, _name##_lbt, _event, SEL_OCCUP1_ID, 6), \ + _CMN_EVENT_HNS(_model, _name##_hbt, _event, SEL_OCCUP1_ID, 7), \ + _CMN_EVENT_HNS(CMNS3R2, _name##_rnf, _event, SEL_OCCUP1_ID, 8), \ + _CMN_EVENT_HNS(CMNS3R2, _name##_rni, _event, SEL_OCCUP1_ID, 9), \ + _CMN_EVENT_HNS(CMNS3R2, _name##_ccglcn, _event, SEL_OCCUP1_ID, 10), \ + _CMN_EVENT_HNS(CMNS3R2, _name##_ccgrn, _event, SEL_OCCUP1_ID, 11) #define CMN_EVENT_HNS_CLS( _name, _event) \ CMN_EVENT_HN_CLS(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event) -#define CMN_EVENT_HNS_SNT(_name, _event) \ - CMN_EVENT_HN_SNT(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event) -#define CMN_EVENT_HNS_HBT(_name, _event) \ - _CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_all, CMN_TYPE_HNS, _event, 0, SEL_HBT_LBT_SEL), \ - _CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_hbt, CMN_TYPE_HNS, _event, 1, SEL_HBT_LBT_SEL), \ - _CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_lbt, CMN_TYPE_HNS, _event, 2, SEL_HBT_LBT_SEL) -#define CMN_EVENT_HNS_SNH(_name, _event) \ - _CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_all, CMN_TYPE_HNS, _event, 0, SEL_SN_HOME_SEL), \ - _CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_sn, CMN_TYPE_HNS, _event, 1, SEL_SN_HOME_SEL), \ - _CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_home, CMN_TYPE_HNS, _event, 2, SEL_SN_HOME_SEL) +#define CMN_EVENT_HNSR0_CLS( _name, _event) \ + CMN_EVENT_HN_CLS(CMN700 | CMNS3R01, hns_##_name, CMN_TYPE_HNS, _event) +#define CMN_EVENT_HNS_SNT(_model, _name, _event) \ + CMN_EVENT_HN_SNT(_model, hns_##_name, CMN_TYPE_HNS, _event) +#define CMN_EVENT_HNS_SNH(_model, _name, _event) \ + _CMN_EVENT_HNS(_model, _name##_all, _event, SEL_SN_HOME_SEL, 0), \ + _CMN_EVENT_HNS(_model, _name##_sn, _event, SEL_SN_HOME_SEL, 1), \ + _CMN_EVENT_HNS(_model, _name##_home, _event, SEL_SN_HOME_SEL, 2) +#define CMN_EVENT_HNS_VC(_name, _event) \ + CMN_EVENT_HNSR0(_name, _event), \ + _CMN_EVENT_HNS(CMNS3R2, _name##_vc0, _event, SEL_SNP_VC_SEL, 0), \ + _CMN_EVENT_HNS(CMNS3R2, _name##_vc1, _event, SEL_SNP_VC_SEL, 1), \ + _CMN_EVENT_HNS(CMNS3R2, _name##_vc2, _event, SEL_SNP_VC_SEL, 2) +#define CMN_EVENT_HNS_ENHBT(_name, _event) \ + _CMN_EVENT_HNS_HBT(CMNS3R2, _name, _event, SEL_ENHANCED_HBT_LBT_SEL), \ + _CMN_EVENT_HNS(CMNS3R2, _name##_rnf, _event, SEL_ENHANCED_HBT_LBT_SEL, 3), \ + _CMN_EVENT_HNS(CMNS3R2, _name##_rni, _event, SEL_ENHANCED_HBT_LBT_SEL, 4), \ + _CMN_EVENT_HNS(CMNS3R2, _name##_ccglcn, _event, SEL_ENHANCED_HBT_LBT_SEL, 5), \ + _CMN_EVENT_HNS(CMNS3R2, _name##_ccgrn, _event, SEL_ENHANCED_HBT_LBT_SEL, 6) +#define CMN_EVENT_HNS_EVICT(_model, _name, _event) \ + _CMN_EVENT_HNS_HBT2(_model, _name##_all, _event, SEL_EVICT_STATE_SEL, 0), \ + _CMN_EVENT_HNS_HBT2(_model, _name##_eu, _event, SEL_EVICT_STATE_SEL, 1), \ + _CMN_EVENT_HNS_HBT2(_model, _name##_en, _event, SEL_EVICT_STATE_SEL, 2), \ + _CMN_EVENT_HNS_HBT2(_model, _name##_su, _event, SEL_EVICT_STATE_SEL, 3), \ + _CMN_EVENT_HNS_HBT2(_model, _name##_sn, _event, SEL_EVICT_STATE_SEL, 4), \ + _CMN_EVENT_HNS_HBT2(_model, _name##_mu, _event, SEL_EVICT_STATE_SEL, 5), \ + _CMN_EVENT_HNS_HBT2(_model, _name##_mn, _event, SEL_EVICT_STATE_SEL, 6) + +#define CMN_EVENT_HNSR0_HBT(_name, _event) \ + _CMN_EVENT_HNS_HBT(CMN700 | CMNS3R01, _name, _event, SEL_HBT_LBT_SEL) +#define CMN_EVENT_HNS_R2SNH(_name, _event) \ + CMN_EVENT_HNSR0(_name, _event), \ + CMN_EVENT_HNS_SNH(CMNS3R2, _name, _event) +#define CMN_EVENT_HNS_R2HBT(_name, _event) \ + CMN_EVENT_HNSR0(_name, _event), \ + _CMN_EVENT_HNS_HBT(CMNS3R2, _name, _event, SEL_HBT_LBT_SEL) +#define CMN_EVENT_HNS_HBT_ENHBT(_name, _event) \ + CMN_EVENT_HNSR0_HBT(_name, _event), \ + CMN_EVENT_HNS_ENHBT(_name, _event) +#define CMN_EVENT_HNS_HBT_OCC(_name, _event) \ + CMN_EVENT_HNSR0_HBT(_name, _event), \ + CMN_EVENT_HNS_OCC(CMNS3R2, _name, _event) +#define CMN_EVENT_HNS_HBT_EVICT(_name, _event) \ + CMN_EVENT_HNSR0_HBT(_name, _event), \ + CMN_EVENT_HNS_EVICT(CMNS3R2, _name, _event) #define _CMN_EVENT_XP_MESH(_name, _event) \ __CMN_EVENT_XP(e_##_name, (_event) | (0 << 2)), \ @@ -1210,65 +1388,72 @@ static struct attribute *arm_cmn_event_attrs[] = { CMN_EVENT_CCLA(pfwd_sndr_stalls_static_crd, 0x2a), CMN_EVENT_CCLA(pfwd_sndr_stalls_dynmaic_crd, 0x2b), - CMN_EVENT_HNS_HBT(cache_miss, 0x01), - CMN_EVENT_HNS_HBT(slc_sf_cache_access, 0x02), - CMN_EVENT_HNS_HBT(cache_fill, 0x03), - CMN_EVENT_HNS_HBT(pocq_retry, 0x04), - CMN_EVENT_HNS_HBT(pocq_reqs_recvd, 0x05), - CMN_EVENT_HNS_HBT(sf_hit, 0x06), - CMN_EVENT_HNS_HBT(sf_evictions, 0x07), - CMN_EVENT_HNS(dir_snoops_sent, 0x08), - CMN_EVENT_HNS(brd_snoops_sent, 0x09), - CMN_EVENT_HNS_HBT(slc_eviction, 0x0a), - CMN_EVENT_HNS_HBT(slc_fill_invalid_way, 0x0b), - CMN_EVENT_HNS(mc_retries_local, 0x0c), - CMN_EVENT_HNS_SNH(mc_reqs_local, 0x0d), + CMN_EVENT_HNS_HBT_ENHBT(cache_miss, 0x01), + CMN_EVENT_HNS_HBT_ENHBT(slc_sf_cache_access, 0x02), + CMN_EVENT_HNS_HBT_ENHBT(cache_fill, 0x03), + CMN_EVENT_HNS_HBT_OCC(pocq_retry, 0x04), + CMN_EVENT_HNS_HBT_OCC(pocq_reqs_recvd, 0x05), + CMN_EVENT_HNS_HBT_ENHBT(sf_hit, 0x06), + CMN_EVENT_HNS_HBT_EVICT(sf_evictions, 0x07), + CMN_EVENT_HNS_VC(dir_snoops_sent, 0x08), + CMN_EVENT_HNS_VC(brd_snoops_sent, 0x09), + CMN_EVENT_HNS_HBT_EVICT(slc_eviction, 0x0a), + CMN_EVENT_HNS_HBT_ENHBT(slc_fill_invalid_way, 0x0b), + CMN_EVENT_HNS_R2SNH(mc_retries_local, 0x0c), + CMN_EVENT_HNS_SNH(CMN_ANY, mc_reqs_local, 0x0d), CMN_EVENT_HNS(qos_hh_retry, 0x0e), - CMN_EVENT_HNS_OCC(qos_pocq_occupancy, 0x0f), - CMN_EVENT_HNS(pocq_addrhaz, 0x10), - CMN_EVENT_HNS(pocq_atomic_addrhaz, 0x11), - CMN_EVENT_HNS(ld_st_swp_adq_full, 0x12), - CMN_EVENT_HNS(cmp_adq_full, 0x13), + CMN_EVENT_HNS_OCC(CMN_ANY, qos_pocq_occupancy, 0x0f), + CMN_EVENT_HNS_HBT_ENHBT(pocq_addrhaz, 0x10), + CMN_EVENT_HNS_HBT_ENHBT(pocq_atomic_addrhaz, 0x11), + CMN_EVENT_HNSR0(ld_st_swp_adq_full, 0x12), + CMN_EVENT_HNSR0(cmp_adq_full, 0x13), CMN_EVENT_HNS(txdat_stall, 0x14), CMN_EVENT_HNS(txrsp_stall, 0x15), - CMN_EVENT_HNS(seq_full, 0x16), + CMN_EVENT_HNSR0(seq_full, 0x16), CMN_EVENT_HNS(seq_hit, 0x17), - CMN_EVENT_HNS(snp_sent, 0x18), - CMN_EVENT_HNS(sfbi_dir_snp_sent, 0x19), - CMN_EVENT_HNS(sfbi_brd_snp_sent, 0x1a), + CMN_EVENT_HNS_VC(snp_sent, 0x18), + CMN_EVENT_HNS_VC(sfbi_dir_snp_sent, 0x19), + CMN_EVENT_HNS_VC(sfbi_brd_snp_sent, 0x1a), CMN_EVENT_HNS(intv_dirty, 0x1c), - CMN_EVENT_HNS(stash_snp_sent, 0x1d), - CMN_EVENT_HNS(stash_data_pull, 0x1e), - CMN_EVENT_HNS(snp_fwded, 0x1f), - CMN_EVENT_HNS(atomic_fwd, 0x20), + CMN_EVENT_HNSR0(stash_snp_sent, 0x1d), + CMN_EVENT_HNSR0(stash_data_pull, 0x1e), + CMN_EVENT_HNS_VC(snp_fwded, 0x1f), + CMN_EVENT_HNSR0(atomic_fwd, 0x20), CMN_EVENT_HNS(mpam_hardlim, 0x21), CMN_EVENT_HNS(mpam_softlim, 0x22), - CMN_EVENT_HNS(snp_sent_cluster, 0x23), - CMN_EVENT_HNS(sf_imprecise_evict, 0x24), + CMN_EVENT_HNS_VC(snp_sent_cluster, 0x23), + CMN_EVENT_HNS_R2HBT(sf_imprecise_evict, 0x24), CMN_EVENT_HNS(sf_evict_shared_line, 0x25), CMN_EVENT_HNS_CLS(pocq_class_occup, 0x26), CMN_EVENT_HNS_CLS(pocq_class_retry, 0x27), CMN_EVENT_HNS_CLS(class_mc_reqs_local, 0x28), - CMN_EVENT_HNS_CLS(class_cgnt_cmin, 0x29), - CMN_EVENT_HNS_SNT(sn_throttle, 0x2a), - CMN_EVENT_HNS_SNT(sn_throttle_min, 0x2b), + CMN_EVENT_HNSR0_CLS(class_cgnt_cmin, 0x29), + CMN_EVENT_HNS_SNT(CMN_ANY, sn_throttle, 0x2a), + CMN_EVENT_HNS_SNT(CMN_ANY, sn_throttle_min, 0x2b), CMN_EVENT_HNS(sf_precise_to_imprecise, 0x2c), CMN_EVENT_HNS(snp_intv_cln, 0x2d), CMN_EVENT_HNS(nc_excl, 0x2e), - CMN_EVENT_HNS(excl_mon_ovfl, 0x2f), + CMN_EVENT_HNSR0(excl_mon_ovfl, 0x2f), CMN_EVENT_HNS(snp_req_recvd, 0x30), CMN_EVENT_HNS(snp_req_byp_pocq, 0x31), CMN_EVENT_HNS(dir_ccgha_snp_sent, 0x32), CMN_EVENT_HNS(brd_ccgha_snp_sent, 0x33), - CMN_EVENT_HNS(ccgha_snp_stall, 0x34), + CMN_EVENT_HNSR0(ccgha_snp_stall, 0x34), CMN_EVENT_HNS(lbt_req_hardlim, 0x35), CMN_EVENT_HNS(hbt_req_hardlim, 0x36), CMN_EVENT_HNS(sf_reupdate, 0x37), - CMN_EVENT_HNS(excl_sf_imprecise, 0x38), + CMN_EVENT_HNS_R2HBT(excl_sf_imprecise, 0x38), CMN_EVENT_HNS(snp_pocq_addrhaz, 0x39), - CMN_EVENT_HNS(mc_retries_remote, 0x3a), - CMN_EVENT_HNS_SNH(mc_reqs_remote, 0x3b), + CMN_EVENT_HNS_R2SNH(mc_retries_remote, 0x3a), + CMN_EVENT_HNS_SNH(CMN_ANY, mc_reqs_remote, 0x3b), CMN_EVENT_HNS_CLS(class_mc_reqs_remote, 0x3c), + CMN_EVENT_HNS_ENHBT(readonce_hazard_detected, 0x3d), + CMN_EVENT_HNS_ENHBT(readonce_fwd_data_completed, 0x3e), + CMN_EVENT_HNS_SNT(CMNS3R2, cbusy00, 0x40), + CMN_EVENT_HNS_SNT(CMNS3R2, cbusy01, 0x41), + CMN_EVENT_HNS_SNT(CMNS3R2, cbusy10, 0x42), + CMN_EVENT_HNS_SNT(CMNS3R2, cbusy11, 0x43), + CMN_EVENT_HNS_ENHBT(ro_rnsd_new_alloc_hint, 0x44), NULL }; @@ -1301,9 +1486,10 @@ static ssize_t arm_cmn_format_show(struct device *dev, static struct attribute *arm_cmn_format_attrs[] = { CMN_FORMAT_ATTR(type, CMN_CONFIG_TYPE), CMN_FORMAT_ATTR(eventid, CMN_CONFIG_EVENTID), - CMN_FORMAT_ATTR(occupid, CMN_CONFIG_OCCUPID), + CMN_FORMAT_ATTR(filter, CMN_CONFIG_FILTER), CMN_FORMAT_ATTR(bynodeid, CMN_CONFIG_BYNODEID), CMN_FORMAT_ATTR(nodeid, CMN_CONFIG_NODEID), + CMN_FORMAT_ATTR(filter2, CMN_CONFIG_FILTER2), CMN_FORMAT_ATTR(wp_dev_sel, CMN_CONFIG_WP_DEV_SEL), CMN_FORMAT_ATTR(wp_chn_sel, CMN_CONFIG_WP_CHN_SEL), @@ -1314,6 +1500,9 @@ static struct attribute *arm_cmn_format_attrs[] = { _CMN_FORMAT_ATTR(wp_val, 1, CMN_CONFIG1_WP_VAL), _CMN_FORMAT_ATTR(wp_mask, 2, CMN_CONFIG2_WP_MASK), + /* Old name for UAPI compatibility */ + CMN_FORMAT_ATTR(occupid, CMN_CONFIG_FILTER), + NULL }; @@ -1377,7 +1566,7 @@ static int arm_cmn_get_assigned_wp_idx(struct perf_event *event, struct arm_cmn_hw_event *hw, unsigned int pos) { - return CMN_EVENT_EVENTID(event) + arm_cmn_get_wp_idx(hw->wp_idx, pos); + return CMN_EVENT_EVENTID(event) + arm_cmn_get_wp_idx(hw, pos); } static void arm_cmn_claim_wp_idx(struct arm_cmn_dtm *dtm, @@ -1388,7 +1577,7 @@ static void arm_cmn_claim_wp_idx(struct arm_cmn_dtm *dtm, struct arm_cmn_hw_event *hw = to_cmn_hw(event); dtm->wp_event[wp_idx] = hw->dtc_idx[dtc]; - arm_cmn_set_wp_idx(hw->wp_idx, pos, wp_idx - CMN_EVENT_EVENTID(event)); + arm_cmn_set_wp_idx(hw, pos, wp_idx - CMN_EVENT_EVENTID(event)); } static u32 arm_cmn_wp_config(struct perf_event *event, int wp_idx) @@ -1459,7 +1648,7 @@ static u64 arm_cmn_read_dtm(struct arm_cmn *cmn, struct arm_cmn_hw_event *hw, dtm = &cmn->dtms[dn->dtm] + hw->dtm_offset; reg = readq_relaxed(dtm->base + offset); } - dtm_idx = arm_cmn_get_index(hw->dtm_idx, i); + dtm_idx = arm_cmn_get_dtm_idx(hw, i); count += (u16)(reg >> (dtm_idx * 16)); } return count; @@ -1506,7 +1695,7 @@ static void arm_cmn_event_read(struct perf_event *event) unsigned long flags; if (CMN_EVENT_TYPE(event) == CMN_TYPE_DTC) { - delta = arm_cmn_read_cc(cmn->dtc + hw->dtc_idx[0]); + delta = arm_cmn_read_cc(cmn->dtc + hw->cc_idx); local64_add(delta, &event->count); return; } @@ -1525,30 +1714,23 @@ static void arm_cmn_event_read(struct perf_event *event) } static int arm_cmn_set_event_sel_hi(struct arm_cmn_node *dn, - enum cmn_filter_select fsel, u8 occupid) + enum cmn_filter_select fsel, u8 val) { - u64 reg; + if (!dn->filter[fsel].count) { + const u64 *filter = arm_cmn_filters[dn->filter[SEL_NONE].val]; + u64 reg = 0; - if (fsel == SEL_NONE) - return 0; + dn->filter[fsel].val = val; + for (int i = SEL_OCCUP1_ID; i < SEL_MAX; i++) { + if (filter[i]) + reg |= field_prep(filter[i], dn->filter[i].val); + } - if (!dn->occupid[fsel].count) { - dn->occupid[fsel].val = occupid; - reg = FIELD_PREP(CMN__PMU_CBUSY_SNTHROTTLE_SEL, - dn->occupid[SEL_CBUSY_SNTHROTTLE_SEL].val) | - FIELD_PREP(CMN__PMU_SN_HOME_SEL, - dn->occupid[SEL_SN_HOME_SEL].val) | - FIELD_PREP(CMN__PMU_HBT_LBT_SEL, - dn->occupid[SEL_HBT_LBT_SEL].val) | - FIELD_PREP(CMN__PMU_CLASS_OCCUP_ID, - dn->occupid[SEL_CLASS_OCCUP_ID].val) | - FIELD_PREP(CMN__PMU_OCCUP1_ID, - dn->occupid[SEL_OCCUP1ID].val); writel_relaxed(reg >> 32, dn->pmu_base + CMN_PMU_EVENT_SEL + 4); - } else if (dn->occupid[fsel].val != occupid) { + } else if (dn->filter[fsel].val != val) { return -EBUSY; } - dn->occupid[fsel].count++; + dn->filter[fsel].count++; return 0; } @@ -1573,7 +1755,7 @@ static void arm_cmn_event_start(struct perf_event *event, int flags) int i; if (type == CMN_TYPE_DTC) { - struct arm_cmn_dtc *dtc = cmn->dtc + hw->dtc_idx[0]; + struct arm_cmn_dtc *dtc = cmn->dtc + hw->cc_idx; writel_relaxed(CMN_DT_DTC_CTL_DT_EN | CMN_DT_DTC_CTL_CG_DISABLE, dtc->base + CMN_DT_DTC_CTL); @@ -1591,7 +1773,7 @@ static void arm_cmn_event_start(struct perf_event *event, int flags) 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); + int dtm_idx = arm_cmn_get_dtm_idx(hw, i); arm_cmn_set_event_sel_lo(dn, dtm_idx, CMN_EVENT_EVENTID(event), hw->wide_sel); @@ -1607,7 +1789,7 @@ static void arm_cmn_event_stop(struct perf_event *event, int flags) int i; if (type == CMN_TYPE_DTC) { - struct arm_cmn_dtc *dtc = cmn->dtc + hw->dtc_idx[0]; + struct arm_cmn_dtc *dtc = cmn->dtc + hw->cc_idx; dtc->cc_active = false; writel_relaxed(CMN_DT_DTC_CTL_DT_EN, dtc->base + CMN_DT_DTC_CTL); @@ -1620,7 +1802,7 @@ static void arm_cmn_event_stop(struct perf_event *event, int flags) 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); + int dtm_idx = arm_cmn_get_dtm_idx(hw, i); arm_cmn_set_event_sel_lo(dn, dtm_idx, 0, hw->wide_sel); } @@ -1630,7 +1812,7 @@ static void arm_cmn_event_stop(struct perf_event *event, int flags) struct arm_cmn_val { u8 dtm_count[CMN_MAX_DTMS]; - u8 occupid[CMN_MAX_DTMS][SEL_MAX]; + u8 filter[CMN_MAX_DTMS][SEL_MAX]; u8 wp[CMN_MAX_DTMS][4]; u8 wp_combine[CMN_MAX_DTMS][2]; int dtc_count[CMN_MAX_DTCS]; @@ -1674,8 +1856,10 @@ static void arm_cmn_val_add_event(struct arm_cmn *cmn, struct arm_cmn_val *val, val->dtm_count[dtm]++; - if (sel > SEL_NONE) - val->occupid[dtm][sel] = CMN_EVENT_OCCUPID(event) + 1; + if (sel) + val->filter[dtm][sel] = CMN_EVENT_FILTER(event) + 1; + if (sel == SEL_EVICT_STATE_SEL) + val->filter[dtm][SEL_HBT_LBT_SEL] = CMN_EVENT_FILTER2(event) + 1; if (type != CMN_TYPE_WP) continue; @@ -1726,8 +1910,12 @@ static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event) if (val->dtm_count[dtm] == CMN_DTM_NUM_COUNTERS) goto done; - if (sel > SEL_NONE && val->occupid[dtm][sel] && - val->occupid[dtm][sel] != CMN_EVENT_OCCUPID(event) + 1) + if (sel && val->filter[dtm][sel] && + val->filter[dtm][sel] != CMN_EVENT_FILTER(event) + 1) + goto done; + + if (sel == SEL_EVICT_STATE_SEL && val->filter[dtm][SEL_HBT_LBT_SEL] && + val->filter[dtm][SEL_HBT_LBT_SEL] != CMN_EVENT_FILTER2(event) + 1) goto done; if (type != CMN_TYPE_WP) @@ -1748,9 +1936,9 @@ static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event) return ret; } -static enum cmn_filter_select arm_cmn_filter_sel(const struct arm_cmn *cmn, - enum cmn_node_type type, - unsigned int eventid) +static enum cmn_filter_select arm_cmn_event_filter(const struct arm_cmn *cmn, + enum cmn_node_type type, + unsigned int eventid) { struct arm_cmn_event_attr *e; enum cmn_model model = arm_cmn_model(cmn); @@ -1758,12 +1946,20 @@ static enum cmn_filter_select arm_cmn_filter_sel(const struct arm_cmn *cmn, for (int i = 0; i < ARRAY_SIZE(arm_cmn_event_attrs) - 1; i++) { e = container_of(arm_cmn_event_attrs[i], typeof(*e), attr.attr); if (e->model & model && e->type == type && e->eventid == eventid) - return e->fsel; + return e->filter[0].sel; } return SEL_NONE; } +static void arm_cmn_event_destroy(struct perf_event *event) +{ + struct arm_cmn_hw_event *hw = to_cmn_hw(event); + + bitmap_free(hw->dtm_idx); + bitmap_free(hw->wp_idx); +} + static int arm_cmn_event_init(struct perf_event *event) { struct arm_cmn *cmn = to_cmn(event->pmu); @@ -1788,6 +1984,11 @@ static int arm_cmn_event_init(struct perf_event *event) if (type == CMN_TYPE_DTC) return arm_cmn_validate_group(cmn, event); + event->destroy = arm_cmn_event_destroy; + hw->dtm_idx = arm_cmn_alloc_dtm_idx(); + if (!hw->dtm_idx) + return -ENOMEM; + eventid = CMN_EVENT_EVENTID(event); /* For watchpoints we need the actual XP node here */ if (type == CMN_TYPE_WP) { @@ -1798,6 +1999,9 @@ static int arm_cmn_event_init(struct perf_event *event) /* ...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; + hw->wp_idx = arm_cmn_alloc_wp_idx(); + if (!hw->wp_idx) + return -ENOMEM; } else if (type == CMN_TYPE_XP && (cmn->part == PART_CMN700 || cmn->part == PART_CMN_S3)) { hw->wide_sel = true; @@ -1807,7 +2011,7 @@ static int arm_cmn_event_init(struct perf_event *event) } /* This is sufficiently annoying to recalculate, so cache it */ - hw->filter_sel = arm_cmn_filter_sel(cmn, type, eventid); + hw->filter_sel = arm_cmn_event_filter(cmn, type, eventid); bynodeid = CMN_EVENT_BYNODEID(event); nodeid = CMN_EVENT_NODEID(event); @@ -1848,7 +2052,7 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event, while (i--) { 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); + unsigned int dtm_idx = arm_cmn_get_dtm_idx(hw, i); if (type == CMN_TYPE_WP) { int wp_idx = arm_cmn_get_assigned_wp_idx(event, hw, i); @@ -1856,19 +2060,36 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event, dtm->wp_event[wp_idx] = -1; } - if (hw->filter_sel > SEL_NONE) - hw->dn[i].occupid[hw->filter_sel].count--; + if (hw->filter_sel) + hw->dn[i].filter[hw->filter_sel].count--; + if (hw->filter_sel == SEL_EVICT_STATE_SEL) + hw->dn[i].filter[SEL_HBT_LBT_SEL].count--; dtm->pmu_config_low &= ~CMN__PMEVCNT_PAIRED(dtm_idx); writel_relaxed(dtm->pmu_config_low, dtm->base + CMN_DTM_PMU_CONFIG); } - memset(hw->dtm_idx, 0, sizeof(hw->dtm_idx)); - memset(hw->wp_idx, 0, sizeof(hw->wp_idx)); + arm_cmn_clear_idx(hw); for_each_hw_dtc_idx(hw, j, idx) cmn->dtc[j].counters[idx] = NULL; } +static int arm_cmn_set_event_filter(struct arm_cmn_node *dn, struct perf_event *event) +{ + enum cmn_filter_select fsel = to_cmn_hw(event)->filter_sel; + int ret = 0; + + if (fsel) + ret = arm_cmn_set_event_sel_hi(dn, fsel, CMN_EVENT_FILTER(event)); + + if (fsel == SEL_EVICT_STATE_SEL && !ret) { + ret = arm_cmn_set_event_sel_hi(dn, SEL_HBT_LBT_SEL, CMN_EVENT_FILTER2(event)); + if (ret) + dn->filter[fsel].count--; + } + return ret; +} + static int arm_cmn_event_add(struct perf_event *event, int flags) { struct arm_cmn *cmn = to_cmn(event->pmu); @@ -1883,7 +2104,7 @@ static int arm_cmn_event_add(struct perf_event *event, int flags) return -ENOSPC; cmn->dtc[i].cycles = event; - hw->dtc_idx[0] = i; + hw->cc_idx = i; if (flags & PERF_EF_START) arm_cmn_event_start(event, 0); @@ -1944,11 +2165,11 @@ static int arm_cmn_event_add(struct perf_event *event, int flags) input_sel = CMN__PMEVCNT0_INPUT_SEL_DEV + dtm_idx + (nid.port << 4) + (nid.dev << 2); - if (arm_cmn_set_event_sel_hi(dn, hw->filter_sel, CMN_EVENT_OCCUPID(event))) + if (arm_cmn_set_event_filter(dn, event)) goto free_dtms; } - arm_cmn_set_index(hw->dtm_idx, i, dtm_idx); + arm_cmn_set_dtm_idx(hw, i, dtm_idx); dtm->input_sel[dtm_idx] = input_sel; shift = CMN__PMEVCNTn_GLOBAL_NUM_SHIFT(dtm_idx); @@ -1981,7 +2202,7 @@ static void arm_cmn_event_del(struct perf_event *event, int flags) arm_cmn_event_stop(event, PERF_EF_UPDATE); if (type == CMN_TYPE_DTC) - cmn->dtc[hw->dtc_idx[0]].cycles = NULL; + cmn->dtc[hw->cc_idx].cycles = NULL; else arm_cmn_event_clear(cmn, event, hw->num_dns); } @@ -2222,6 +2443,18 @@ static unsigned int arm_cmn_dtc_domain(struct arm_cmn *cmn, void __iomem *xp_reg return FIELD_GET(CMN_DTM_UNIT_INFO_DTC_DOMAIN, readl_relaxed(xp_region + offset)); } +static unsigned int arm_cmn_graviton5_dtc_domain(u16 xp_id) +{ + unsigned int x = (xp_id >> 7) & 0xf; + unsigned int y = (xp_id >> 3) & 0xf; + + /* + * The unit info register reads as zero; derive the DTC domain from + * the XP's mesh coordinates over the 10x14 mesh. + */ + return (x / 5) + (y / 7) * 2; +} + static void arm_cmn_init_node_info(struct arm_cmn *cmn, u32 offset, struct arm_cmn_node *node) { int level; @@ -2262,11 +2495,13 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) void __iomem *cfg_region, __iomem *xp_region; struct arm_cmn_node cfg, *dn; struct arm_cmn_dtm *dtm; + enum cmn_model model; enum cmn_part part; u16 child_count, child_poff; u64 reg; int i, j; size_t sz; + bool graviton5_workaround = false; arm_cmn_init_node_info(cmn, rgn_offset, &cfg); if (cfg.type != CMN_TYPE_CFG) @@ -2277,6 +2512,13 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) reg = readq_relaxed(cfg_region + CMN_CFGM_PERIPH_ID_01); part = FIELD_GET(CMN_CFGM_PID0_PART_0, reg); part |= FIELD_GET(CMN_CFGM_PID1_PART_1, reg) << 8; + + /* Graviton5 has a customised CMN-S3 which needs some fixups */ + if (cmn->part == PART_GRAVITON5) { + cmn->part = PART_CMN_S3; + graviton5_workaround = true; + } + /* 600AE is close enough that it's not really worth more complexity */ if (part == PART_CMN600AE) part = PART_CMN600; @@ -2285,30 +2527,44 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) "Firmware binding mismatch: expected part number 0x%x, found 0x%x\n", cmn->part, part); cmn->part = part; - if (!arm_cmn_model(cmn)) - dev_warn(cmn->dev, "Unknown part number: 0x%x\n", part); reg = readl_relaxed(cfg_region + CMN_CFGM_PERIPH_ID_23); cmn->rev = FIELD_GET(CMN_CFGM_PID2_REVISION, reg); + model = arm_cmn_model(cmn); + if (!model) + dev_warn(cmn->dev, "Unknown part number: 0x%x\n", part); + /* * With the device isolation feature, if firmware has neglected to enable * an XP port then we risk locking up if we try to access anything behind - * it; however we also have no way to tell from Non-Secure whether any - * given port is disabled or not, so the only way to win is not to play... + * it; however prior to CMN S3 r2p0 we also have no way to tell from + * Non-Secure whether any given port is disabled or not, so in that case + * the only way to win is not to play... */ reg = readq_relaxed(cfg_region + CMN_CFGM_INFO_GLOBAL); - if (reg & CMN_INFO_DEVICE_ISO_ENABLE) { + if (reg & CMN_INFO_DEVICE_ISO_ENABLE && model == CMNS3R01) { dev_err(cmn->dev, "Device isolation enabled, not continuing due to risk of lockup\n"); return -ENODEV; } - 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); + if (model < CMNS3R2) { + 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); + } else { + cmn->multi_dtm = reg & CMN_S3_R2_MULTIPLE_DTM_EN; + } reg = readq_relaxed(cfg_region + CMN_CFGM_INFO_GLOBAL_1); - cmn->snp_vc_num = FIELD_GET(CMN_INFO_SNP_VC_NUM, reg); - cmn->req_vc_num = FIELD_GET(CMN_INFO_REQ_VC_NUM, reg); + if (model < CMNS3R2) { + cmn->snp_vc_num = FIELD_GET(CMN_INFO_SNP_VC_NUM, reg); + cmn->req_vc_num = FIELD_GET(CMN_INFO_REQ_VC_NUM, reg); + } else { + cmn->rsp_vc_num = FIELD_GET(CMN_S3_R2_RSP_VC_NUM, reg); + cmn->dat_vc_num = FIELD_GET(CMN_S3_R2_DAT_VC_NUM, reg); + cmn->snp_vc_num = FIELD_GET(CMN_S3_R2_SNP_VC_NUM, reg); + cmn->req_vc_num = FIELD_GET(CMN_S3_R2_REQ_VC_NUM, reg); + } reg = readq_relaxed(cfg_region + CMN_CHILD_INFO); child_count = FIELD_GET(CMN_CI_CHILD_COUNT, reg); @@ -2366,6 +2622,8 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) if (cmn->part == PART_CMN600) xp->dtc = -1; + else if (graviton5_workaround) + xp->dtc = arm_cmn_graviton5_dtc_domain(xp->id); else xp->dtc = arm_cmn_dtc_domain(cmn, xp_region); @@ -2406,15 +2664,12 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) reg = readq_relaxed(xp_region + child_poff + j * 8); /* * Don't even try to touch anything external, since in general - * we haven't a clue how to power up arbitrary CHI requesters. - * As of CMN-600r1 these could only be RN-SAMs or CXLAs, - * neither of which have any PMU events anyway. - * (Actually, CXLAs do seem to have grown some events in r1p2, - * but they don't go to regular XP DTMs, and they depend on - * secure configuration which we can't easily deal with) + * we haven't a clue how to power up arbitrary CHI requesters, + * and none of them have standard PMU events anyway. Isolated + * nodes effectively just do not exist at all from our PoV. */ - if (reg & CMN_CHILD_NODE_EXTERNAL) { - dev_dbg(cmn->dev, "ignoring external node %llx\n", reg); + if (reg & (CMN_CHILD_NODE_EXTERNAL | CMN_CHILD_NODE_ISOLATED)) { + dev_dbg(cmn->dev, "ignoring external/isolated node %llx\n", reg); continue; } /* @@ -2441,9 +2696,18 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) dev_err(cmn->dev, "Node ID invalid for supported CMN versions: %d\n", dn->logid); return -ENODEV; } + /* + * We can utilise the "wasted" filter array slot to store + * the index for referencing the filter encodings later. + */ + dn->filter[SEL_NONE].val = arm_cmn_filter(dn->type, model); switch (dn->type) { case CMN_TYPE_DTC: + if (graviton5_workaround) { + /* Node info logical ID is zeroed; use the XP's */ + dn->logid = xp->logid; + } cmn->num_dtcs++; dn++; break; @@ -2659,6 +2923,7 @@ static const struct acpi_device_id arm_cmn_acpi_match[] = { { "ARMHC650" }, { "ARMHC700" }, { "ARMHC003" }, + { "AMZN0070", PART_GRAVITON5 }, {} }; MODULE_DEVICE_TABLE(acpi, arm_cmn_acpi_match); diff --git a/drivers/perf/arm_cspmu/arm_cspmu.c b/drivers/perf/arm_cspmu/arm_cspmu.c index 80fb314d5135..60005f89892d 100644 --- a/drivers/perf/arm_cspmu/arm_cspmu.c +++ b/drivers/perf/arm_cspmu/arm_cspmu.c @@ -78,13 +78,13 @@ static struct acpi_apmt_node *arm_cspmu_apmt_node(struct device *dev) } /* - * In CoreSight PMU architecture, all of the MMIO registers are 32-bit except - * counter register. The counter register can be implemented as 32-bit or 64-bit - * register depending on the value of PMCFGR.SIZE field. For 64-bit access, - * single-copy 64-bit atomic support is implementation defined. APMT node flag - * is used to identify if the PMU supports 64-bit single copy atomic. If 64-bit - * single copy atomic is not supported, the driver treats the register as a pair - * of 32-bit register. + * With FEAT_CSPMU_EXT32, all of the MMIO registers are 32-bit except the + * counter registers, which are either 32-bit or 64-bit depending on the value + * of PMCFGR.SIZE. It is implementation-defined whether single-copy-atomic + * 64-bit accesses are supported, so we rely on a firmware flag to identify + * that, and otherwise treat a 64-bit counter as a non-atomic pair of 32-bit + * registers. With FEAT_CSPMU_EXT64, everything is 64-bit, but we may still + * have to deal with atomicity being broken. */ /* @@ -173,13 +173,30 @@ arm_cspmu_event_attr_is_visible(struct kobject *kobj, eattr = container_of(attr, typeof(*eattr), attr.attr); /* Hide cycle event if not supported */ - if (!supports_cycle_counter(cspmu) && + if ((cspmu->has_ext64 || !supports_cycle_counter(cspmu)) && eattr->id == ARM_CSPMU_EVT_CYCLES_DEFAULT) return 0; return attr->mode; } +ssize_t arm_cspmu_default_format_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct perf_pmu_events_attr *fmt = container_of(attr, typeof(*fmt), attr); + struct arm_cspmu *cspmu = to_arm_cspmu(dev_get_drvdata(dev)); + u64 field = cspmu->has_ext64 ? U64_MAX : U32_MAX; + DECLARE_BITMAP(bits, 64) = { BITMAP_FROM_U64(field) }; + + if (!fmt->id) { + set_bit(32, bits); /* For 32-bit "cycles" event */ + return sysfs_emit(buf, "config:%*pbl\n", 64, bits); + } + + return sysfs_emit(buf, "config%lld:%*pbl\n", fmt->id, 64, bits); +} +EXPORT_SYMBOL_GPL(arm_cspmu_default_format_show); + static struct attribute *arm_cspmu_format_attrs[] = { ARM_CSPMU_FORMAT_EVENT_ATTR, ARM_CSPMU_FORMAT_FILTER_ATTR, @@ -198,9 +215,9 @@ arm_cspmu_get_format_attrs(const struct arm_cspmu *cspmu) return attrs; } -static u32 arm_cspmu_event_type(const struct perf_event *event) +static u64 arm_cspmu_event_type(const struct perf_event *event) { - return event->attr.config & ARM_CSPMU_EVENT_MASK; + return event->attr.config; } static bool arm_cspmu_is_cycle_counter_event(const struct perf_event *event) @@ -208,6 +225,16 @@ static bool arm_cspmu_is_cycle_counter_event(const struct perf_event *event) return (event->attr.config == ARM_CSPMU_EVT_CYCLES_DEFAULT); } +static u64 arm_cspmu_filter(const struct perf_event *event) +{ + return event->attr.config1; +} + +static u64 arm_cspmu_filter2(const struct perf_event *event) +{ + return event->attr.config2; +} + static ssize_t arm_cspmu_identifier_show(struct device *dev, struct device_attribute *attr, char *page) @@ -250,38 +277,43 @@ static const char *arm_cspmu_get_name(const struct arm_cspmu *cspmu) struct device *dev; struct acpi_apmt_node *apmt_node; u8 pmu_type; - char *name; char acpi_hid_string[ACPI_ID_LEN] = { 0 }; - static atomic_t pmu_idx[ACPI_APMT_NODE_TYPE_COUNT] = { 0 }; + static atomic_t pmu_idx; + u32 id; dev = cspmu->dev; apmt_node = arm_cspmu_apmt_node(dev); if (!apmt_node) return devm_kasprintf(dev, GFP_KERNEL, PMUNAME "_%u", - atomic_fetch_inc(&pmu_idx[0])); + atomic_fetch_inc(&pmu_idx)); pmu_type = apmt_node->type; - - if (pmu_type >= ACPI_APMT_NODE_TYPE_COUNT) { + switch (pmu_type) { + default: dev_err(dev, "unsupported PMU type-%u\n", pmu_type); return NULL; - } - - if (pmu_type == ACPI_APMT_NODE_TYPE_ACPI) { + case ACPI_APMT_NODE_TYPE_ACPI: memcpy(acpi_hid_string, &apmt_node->inst_primary, sizeof(apmt_node->inst_primary)); - name = devm_kasprintf(dev, GFP_KERNEL, "%s_%s_%s_%u", PMUNAME, + return devm_kasprintf(dev, GFP_KERNEL, "%s_%s_%s_%u", PMUNAME, arm_cspmu_type_str[pmu_type], acpi_hid_string, apmt_node->inst_secondary); - } else { - name = devm_kasprintf(dev, GFP_KERNEL, "%s_%s_%d", PMUNAME, - arm_cspmu_type_str[pmu_type], - atomic_fetch_inc(&pmu_idx[pmu_type])); + case ACPI_APMT_NODE_TYPE_MC: + id = apmt_node->id; + break; + case ACPI_APMT_NODE_TYPE_SMMU: + case ACPI_APMT_NODE_TYPE_PCIE_ROOT: + id = apmt_node->inst_primary; + break; + case ACPI_APMT_NODE_TYPE_CACHE: + id = apmt_node->inst_secondary; + break; } - return name; + return devm_kasprintf(dev, GFP_KERNEL, "%s_%s_%u", PMUNAME, + arm_cspmu_type_str[pmu_type], id); } static ssize_t arm_cspmu_cpumask_show(struct device *dev, @@ -412,6 +444,17 @@ static int arm_cspmu_init_impl_ops(struct arm_cspmu *cspmu) DEFAULT_IMPL_OP(event_attr_is_visible), }; + /* + * With 64-bit events, since our default "cycles" encoding won't work, + * and the architecture recommends against implementing it anyway, we + * choose to effectively ignore FEAT_CSPMU_CCNTR, unless a vendor + * module really wants to provide its own encoding and ops. + */ + if (cspmu->has_ext64) { + cspmu->impl.ops.is_cycle_counter_event = NULL; + cspmu->impl.ops.set_cc_filter = NULL; + } + /* Firmware may override implementer/product ID from PMIIDR */ if (apmt_node && apmt_node->impl_id) cspmu->impl.pmiidr = apmt_node->impl_id; @@ -432,13 +475,15 @@ static int arm_cspmu_init_impl_ops(struct arm_cspmu *cspmu) if (ret) module_put(match->module); } else { - WARN(1, "arm_cspmu failed to get module: %s\n", + dev_WARN(cspmu->dev, "Failed to get module: %s\n", match->module_name); ret = -EINVAL; } } else { request_module_nowait(match->module_name); - ret = -EPROBE_DEFER; + ret = dev_err_probe(cspmu->dev, -EPROBE_DEFER, + "Waiting for module %s to load\n", + match->module_name); } mutex_unlock(&arm_cspmu_lock); @@ -511,19 +556,24 @@ static int arm_cspmu_alloc_attr_groups(struct arm_cspmu *cspmu) return 0; } +static inline int arm_cspmu_pmcr(struct arm_cspmu *cspmu) +{ + return cspmu->has_ext64 ? PMCR_64 : PMCR; +} + static inline void arm_cspmu_reset_counters(struct arm_cspmu *cspmu) { - writel(PMCR_C | PMCR_P, cspmu->base0 + PMCR); + writel(PMCR_C | PMCR_P, cspmu->base0 + arm_cspmu_pmcr(cspmu)); } static inline void arm_cspmu_start_counters(struct arm_cspmu *cspmu) { - writel(PMCR_E, cspmu->base0 + PMCR); + writel(PMCR_E, cspmu->base0 + arm_cspmu_pmcr(cspmu)); } static inline void arm_cspmu_stop_counters(struct arm_cspmu *cspmu) { - writel(0, cspmu->base0 + PMCR); + writel(0, cspmu->base0 + arm_cspmu_pmcr(cspmu)); } static void arm_cspmu_enable(struct pmu *pmu) @@ -554,7 +604,8 @@ static int arm_cspmu_get_event_idx(struct arm_cspmu_hw_events *hw_events, struct arm_cspmu *cspmu = to_arm_cspmu(event->pmu); if (supports_cycle_counter(cspmu)) { - if (cspmu->impl.ops.is_cycle_counter_event(event)) { + if (cspmu->impl.ops.is_cycle_counter_event && + cspmu->impl.ops.is_cycle_counter_event(event)) { /* Search for available cycle counter. */ if (test_and_set_bit(cspmu->cycle_counter_logical_idx, hw_events->used_ctrs)) @@ -797,26 +848,33 @@ static void arm_cspmu_event_update(struct perf_event *event) static inline void arm_cspmu_set_event(struct arm_cspmu *cspmu, struct hw_perf_event *hwc) { - u32 offset = PMEVTYPER + (4 * hwc->idx); - - writel(hwc->config, cspmu->base0 + offset); + if (cspmu->has_ext64) + writeq(hwc->config, cspmu->base0 + PMEVTYPER + (8 * hwc->idx)); + else + writel(hwc->config, cspmu->base0 + PMEVTYPER + (4 * hwc->idx)); } static void arm_cspmu_set_ev_filter(struct arm_cspmu *cspmu, const struct perf_event *event) { - u32 filter = event->attr.config1 & ARM_CSPMU_FILTER_MASK; - u32 filter2 = event->attr.config2 & ARM_CSPMU_FILTER_MASK; - u32 offset = 4 * event->hw.idx; + u64 filter = arm_cspmu_filter(event); + u64 filter2 = arm_cspmu_filter2(event); + int n = event->hw.idx; - writel(filter, cspmu->base0 + PMEVFILTR + offset); - writel(filter2, cspmu->base0 + PMEVFILT2R + offset); + if (cspmu->has_ext64) { + writeq(filter, cspmu->base0 + PMEVFILTR + (8 * n)); + writeq(filter2, cspmu->base0 + PMEVFILT2R + (8 * n)); + } else { + writel(filter, cspmu->base0 + PMEVFILTR + (4 * n)); + writel(filter2, cspmu->base0 + PMEVFILT2R + (4 * n)); + } } +/* Note we deliberately don't expect 64-bit filters here; see init_impl_ops */ static void arm_cspmu_set_cc_filter(struct arm_cspmu *cspmu, const struct perf_event *event) { - u32 filter = event->attr.config1 & ARM_CSPMU_FILTER_MASK; + u32 filter = arm_cspmu_filter(event); writel(filter, cspmu->base0 + PMCCFILTR); } @@ -969,6 +1027,30 @@ static int arm_cspmu_init_mmio(struct arm_cspmu *cspmu) } } + /* + * We can infer FEAT_CSPMU_EXT64 from PMCNTEN, or hope that anything + * that failed to get that right has at least implemented the optional + * PMDEVARCH correctly... + * + * Note that architecturally, has_ext64 *should* imply has_atomic_dword, + * but enough implementations have ignored that already that we'll just + * have to still rely on the firmware flag. + */ + writel(~0U, cspmu->base0 + PMCNTENCLR); + writel(~0U, cspmu->base0 + PMCNTEN); + if (readl(cspmu->base0 + PMCNTENCLR)) { + cspmu->has_ext64 = true; + writel(0, cspmu->base0 + PMCNTEN); + } else { + u32 reg = readl(cspmu->base0 + PMDEVARCH); + + if (reg & ARM_CSPMU_PMDEVARCH_PRESENT) { + reg &= ARM_CSPMU_PMDEVARCH_ARCHPART; + if (reg == 0xaf4 || reg == 0xaf5) + cspmu->has_ext64 = true; + } + } + cspmu->pmcfgr = readl(cspmu->base0 + PMCFGR); cspmu->num_logical_ctrs = FIELD_GET(PMCFGR_N, cspmu->pmcfgr) + 1; @@ -1070,10 +1152,8 @@ static int arm_cspmu_request_irq(struct arm_cspmu *cspmu) ret = devm_request_irq(dev, irq, arm_cspmu_handle_irq, IRQF_NOBALANCING | IRQF_NO_THREAD, dev_name(dev), cspmu); - if (ret) { - dev_err(dev, "Could not request IRQ %d\n", irq); + if (ret) return ret; - } cspmu->irq = irq; @@ -1249,8 +1329,11 @@ static int arm_cspmu_device_probe(struct platform_device *pdev) return ret; ret = arm_cspmu_request_irq(cspmu); - if (ret) - return ret; + if (ret) { + if (counter_size(cspmu) < 64) + return ret; + dev_info(cspmu->dev, "Continuing without IRQ\n"); + } ret = arm_cspmu_get_cpus(cspmu); if (ret) diff --git a/drivers/perf/arm_cspmu/arm_cspmu.h b/drivers/perf/arm_cspmu/arm_cspmu.h index 3fc5c8d77266..c4058d602477 100644 --- a/drivers/perf/arm_cspmu/arm_cspmu.h +++ b/drivers/perf/arm_cspmu/arm_cspmu.h @@ -35,21 +35,15 @@ PMU_EVENT_ATTR_ID(_name, arm_cspmu_sysfs_event_show, _config) -/* Default event id mask */ -#define ARM_CSPMU_EVENT_MASK GENMASK_ULL(63, 0) - -/* Default filter value mask */ -#define ARM_CSPMU_FILTER_MASK GENMASK_ULL(63, 0) - /* Default event format */ #define ARM_CSPMU_FORMAT_EVENT_ATTR \ - ARM_CSPMU_FORMAT_ATTR(event, "config:0-32") + PMU_EVENT_ATTR_ID(event, arm_cspmu_default_format_show, 0) /* Default filter format */ #define ARM_CSPMU_FORMAT_FILTER_ATTR \ - ARM_CSPMU_FORMAT_ATTR(filter, "config1:0-31") + PMU_EVENT_ATTR_ID(filter, arm_cspmu_default_format_show, 1) #define ARM_CSPMU_FORMAT_FILTER2_ATTR \ - ARM_CSPMU_FORMAT_ATTR(filter2, "config2:0-31") + PMU_EVENT_ATTR_ID(filter2, arm_cspmu_default_format_show, 2) /* * This is the default event number for cycle count, if supported, since the @@ -78,6 +72,7 @@ #define PMEVFILT2R 0x800 #define PMEVFILTR 0xA00 #define PMCNTENSET 0xC00 +#define PMCNTEN 0xC10 #define PMCNTENCLR 0xC20 #define PMINTENSET 0xC40 #define PMINTENCLR 0xC60 @@ -87,6 +82,8 @@ #define PMCFGR 0xE00 #define PMCR 0xE04 #define PMIIDR 0xE08 +#define PMCR_64 0xE10 +#define PMDEVARCH 0xFBC #define PMPIDR0 0xFE0 #define PMPIDR1 0xFE4 #define PMPIDR2 0xFE8 @@ -154,6 +151,10 @@ #define ARM_CSPMU_IMPL_ID_NVIDIA 0x36B #define ARM_CSPMU_IMPL_ID_AMPERE 0xA16 +/* PMDEVARCH */ +#define ARM_CSPMU_PMDEVARCH_PRESENT BIT(20) +#define ARM_CSPMU_PMDEVARCH_ARCHPART GENMASK(11, 0) + struct arm_cspmu; /* This tracks the events assigned to each counter in the PMU. */ @@ -183,7 +184,7 @@ struct arm_cspmu_impl_ops { /* Check if the event corresponds to cycle count event */ bool (*is_cycle_counter_event)(const struct perf_event *event); /* Decode event type/id from configs */ - u32 (*event_type)(const struct perf_event *event); + u64 (*event_type)(const struct perf_event *event); /* Set/reset event filters */ void (*set_cc_filter)(struct arm_cspmu *cspmu, const struct perf_event *event); @@ -234,6 +235,7 @@ struct arm_cspmu { int irq; bool has_atomic_dword; + bool has_ext64; u32 pmcfgr; u32 num_logical_ctrs; u32 num_set_clr_reg; @@ -250,6 +252,9 @@ ssize_t arm_cspmu_sysfs_event_show(struct device *dev, struct device_attribute *attr, char *buf); +ssize_t arm_cspmu_default_format_show(struct device *dev, + struct device_attribute *attr, char *buf); + /* Register vendor backend. */ int arm_cspmu_impl_register(const struct arm_cspmu_impl_match *impl_match); diff --git a/drivers/perf/arm_cspmu/nvidia_cspmu.c b/drivers/perf/arm_cspmu/nvidia_cspmu.c index bac83e424d6d..a4c1ab886709 100644 --- a/drivers/perf/arm_cspmu/nvidia_cspmu.c +++ b/drivers/perf/arm_cspmu/nvidia_cspmu.c @@ -762,7 +762,7 @@ static void pcie_tgt_pmu_reset_ev_filter(struct arm_cspmu *cspmu, pcie_tgt_pmu_config_addr_filter(cspmu, false, base, mask, idx); } -static u32 pcie_tgt_pmu_event_type(const struct perf_event *event) +static u64 pcie_tgt_pmu_event_type(const struct perf_event *event) { return event->attr.config & NV_PCIE_TGT_EV_TYPE_MASK; } diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 939bcbd433aa..aa1dac0b440f 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -931,8 +931,13 @@ int armpmu_register(struct arm_pmu *pmu) /* * By this stage we know our supported CPUs on either DT/ACPI platforms, * detect the SMT implementation. + * On SMT CPUs, the PMCCNTR_EL0 increments from the processor clock rather + * than the PE clock (ARM DDI0487 L.b D13.1.3) which means it'll continue + * counting on a WFI PE if one of its SMT sibling is not idle on a + * multi-threaded implementation. So don't use it on SMT cores. */ - pmu->has_smt = topology_core_has_smt(cpumask_first(&pmu->supported_cpus)); + pmu->avoid_pmccntr |= + topology_core_has_smt(cpumask_first(&pmu->supported_cpus)); if (!pmu->set_event_filter) pmu->pmu.capabilities |= PERF_PMU_CAP_NO_EXCLUDE; diff --git a/drivers/perf/arm_pmu_acpi.c b/drivers/perf/arm_pmu_acpi.c index e80f76d95e68..ca6bf8f86cfe 100644 --- a/drivers/perf/arm_pmu_acpi.c +++ b/drivers/perf/arm_pmu_acpi.c @@ -22,7 +22,7 @@ static DEFINE_PER_CPU(int, pmu_irqs); static int arm_pmu_acpi_register_irq(int cpu) { struct acpi_madt_generic_interrupt *gicc; - int gsi, trigger; + int gsi; gicc = acpi_cpu_get_madt_gicc(cpu); @@ -38,11 +38,6 @@ static int arm_pmu_acpi_register_irq(int cpu) if (!gsi) return 0; - if (gicc->flags & ACPI_MADT_PERFORMANCE_IRQ_MODE) - trigger = ACPI_EDGE_SENSITIVE; - else - trigger = ACPI_LEVEL_SENSITIVE; - /* * Helpfully, the MADT GICC doesn't have a polarity flag for the * "performance interrupt". Luckily, on compliant GICs the polarity is @@ -53,8 +48,12 @@ static int arm_pmu_acpi_register_irq(int cpu) * may not match the real polarity, but that should not matter. * * Other interrupt controllers are not supported with ACPI. + * + * The spec also indicates that the PMU interrupt can be edge + * triggered, which doesn't make any sense (SW needs to clear the + * interrupt condition for the level to drop). Ignore the silly flag. */ - return acpi_register_gsi(NULL, gsi, trigger, ACPI_ACTIVE_HIGH); + return acpi_register_gsi(NULL, gsi, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_HIGH); } static void arm_pmu_acpi_unregister_irq(int cpu) diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 8014ff766cff..03359e078301 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -8,6 +8,7 @@ * This code is based heavily on the ARMv7 perf event code. */ +#include #include #include #include @@ -795,6 +796,7 @@ static void armv8pmu_disable_user_access(void) static void armv8pmu_enable_user_access(struct arm_pmu *cpu_pmu) { int i; + u64 userenr = ARMV8_PMU_USERENR_ER | ARMV8_PMU_USERENR_UEN; struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events); if (is_pmuv3p9(cpu_pmu->pmuver)) { @@ -817,7 +819,10 @@ static void armv8pmu_enable_user_access(struct arm_pmu *cpu_pmu) } } - update_pmuserenr(ARMV8_PMU_USERENR_ER | ARMV8_PMU_USERENR_CR | ARMV8_PMU_USERENR_UEN); + if (!cpu_pmu->avoid_pmccntr) + userenr |= ARMV8_PMU_USERENR_CR; + + update_pmuserenr(userenr); } static void armv8pmu_enable_event(struct perf_event *event) @@ -1002,13 +1007,7 @@ static bool armv8pmu_can_use_pmccntr(struct pmu_hw_events *cpuc, if (has_branch_stack(event)) return false; - /* - * The PMCCNTR_EL0 increments from the processor clock rather than - * the PE clock (ARM DDI0487 L.b D13.1.3) which means it'll continue - * counting on a WFI PE if one of its SMT sibling is not idle on a - * multi-threaded implementation. So don't use it on SMT cores. - */ - if (cpu_pmu->has_smt) + if (cpu_pmu->avoid_pmccntr) return false; return true; @@ -1250,7 +1249,8 @@ static int __armv8_pmuv3_map_event(struct perf_event *event, if (!(event->attach_state & PERF_ATTACH_TASK)) return -EINVAL; if (armv8pmu_event_is_64bit(event) && - (hw_event_id != ARMV8_PMUV3_PERFCTR_CPU_CYCLES) && + (hw_event_id != ARMV8_PMUV3_PERFCTR_CPU_CYCLES || + armpmu->avoid_pmccntr) && !armv8pmu_has_long_event(armpmu)) return -EOPNOTSUPP; @@ -1299,6 +1299,45 @@ static int armv8_vulcan_map_event(struct perf_event *event) &armv8_vulcan_perf_cache_map); } +#ifdef CONFIG_ARM64 +/* + * List of CPUs that should avoid using PMCCNTR_EL0. + */ +static struct midr_range armv8pmu_avoid_pmccntr_cpus[] = { + /* + * NVIDIA Olympus may expose different WFI/WFE behaviour between the + * PMCCNTR_EL0 and the CPU_CYCLES event on programmable counters. + * While the CPU is in WFI/WFE state, the PMCCNTR_EL0 may still increment + * but the programmable counter may not. This is an implementation specific + * behavior and not an erratum. Perf assumes those two paths are + * interchangeable, so avoid using PMCCNTR_EL0 for CPU_CYCLES event. + * + * From ARM DDI0487 D14.4: + * It is IMPLEMENTATION SPECIFIC whether CPU_CYCLES and PMCCNTR count + * when the PE is in WFI or WFE state, even if the clocks are not stopped. + * + * From ARM DDI0487 D24.5.2: + * All counters are subject to any changes in clock frequency, including + * clock stopping caused by the WFI and WFE instructions. + * This means that it is CONSTRAINED UNPREDICTABLE whether or not + * PMCCNTR_EL0 continues to increment when clocks are stopped by WFI and + * WFE instructions. + */ + MIDR_ALL_VERSIONS(MIDR_NVIDIA_OLYMPUS), + {} +}; + +static bool armv8pmu_is_in_avoid_pmccntr_cpus(void) +{ + return is_midr_in_range_list(armv8pmu_avoid_pmccntr_cpus); +} +#else +static bool armv8pmu_is_in_avoid_pmccntr_cpus(void) +{ + return false; +} +#endif + struct armv8pmu_probe_info { struct arm_pmu *pmu; bool present; @@ -1348,6 +1387,13 @@ static void __armv8pmu_probe_pmu(void *info) else cpu_pmu->reg_pmmir = 0; + /* + * On some CPUs, PMCCNTR_EL0 does not match the behavior of CPU_CYCLES + * programmable counter, so avoid routing cycles through PMCCNTR_EL0 to + * prevent inconsistency in the results. + */ + cpu_pmu->avoid_pmccntr |= armv8pmu_is_in_avoid_pmccntr_cpus(); + brbe_probe(cpu_pmu); } @@ -1361,7 +1407,7 @@ static int branch_records_alloc(struct arm_pmu *armpmu) struct pmu_hw_events *events_cpu; events_cpu = per_cpu_ptr(armpmu->hw_events, cpu); - events_cpu->branch_stack = kmalloc(size, GFP_KERNEL); + events_cpu->branch_stack = kzalloc(size, GFP_KERNEL); if (!events_cpu->branch_stack) return -ENOMEM; } diff --git a/drivers/perf/arm_spe_pmu.c b/drivers/perf/arm_spe_pmu.c index dbd0da111639..b64cf2313a20 100644 --- a/drivers/perf/arm_spe_pmu.c +++ b/drivers/perf/arm_spe_pmu.c @@ -577,7 +577,7 @@ static u64 __arm_spe_pmu_next_off(struct perf_output_handle *handle) * the page boundary following it. Keep the tail boundary if * that's lower. */ - if (handle->wakeup < (handle->head + handle->size) && head <= wakeup) + if ((handle->wakeup - handle->head) < handle->size && head <= wakeup) limit = min(limit, round_up(wakeup, PAGE_SIZE)); if (limit > head) diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c index 68a54d97d2a8..bb32cd1084e9 100644 --- a/drivers/perf/cxl_pmu.c +++ b/drivers/perf/cxl_pmu.c @@ -77,6 +77,10 @@ #define CXL_PMU_GID_S2M_NDR 0x0024 #define CXL_PMU_GID_S2M_DRS 0x0025 #define CXL_PMU_GID_DDR 0x8000 +#define CXL_PMU_GID_QUEUE_OCC 0x8001 +#define CXL_PMU_GID_QUEUE_RESID 0x8002 +#define CXL_PMU_GID_RETRY_EVENTS 0x8003 +#define CXL_PMU_GID_THROTTLE 0x8004 static int cxl_pmu_cpuhp_state_num; @@ -106,6 +110,7 @@ struct cxl_pmu_info { int on_cpu; struct hlist_node node; bool filter_hdm; + bool filter_crb; int irq; }; @@ -142,6 +147,8 @@ static int cxl_pmu_parse_caps(struct device *dev, struct cxl_pmu_info *info) info->num_event_capabilities = FIELD_GET(CXL_PMU_CAP_NUM_EVN_CAP_REG_SUP_MSK, val) + 1; info->filter_hdm = FIELD_GET(CXL_PMU_CAP_FILTERS_SUP_MSK, val) & CXL_PMU_FILTER_HDM; + info->filter_crb = FIELD_GET(CXL_PMU_CAP_FILTERS_SUP_MSK, val) & + CXL_PMU_FILTER_CHAN_RANK_BANK; if (FIELD_GET(CXL_PMU_CAP_INT, val)) info->irq = FIELD_GET(CXL_PMU_CAP_MSI_N_MSK, val); else @@ -225,6 +232,8 @@ enum { cxl_pmu_edge_attr, cxl_pmu_hdm_filter_en_attr, cxl_pmu_hdm_attr, + cxl_pmu_crb_filter_en_attr, + cxl_pmu_crb_attr, }; static struct attribute *cxl_pmu_format_attr[] = { @@ -236,6 +245,8 @@ static struct attribute *cxl_pmu_format_attr[] = { [cxl_pmu_edge_attr] = CXL_PMU_FORMAT_ATTR(edge, "config1:17"), [cxl_pmu_hdm_filter_en_attr] = CXL_PMU_FORMAT_ATTR(hdm_filter_en, "config1:18"), [cxl_pmu_hdm_attr] = CXL_PMU_FORMAT_ATTR(hdm, "config2:0-15"), + [cxl_pmu_crb_filter_en_attr] = CXL_PMU_FORMAT_ATTR(crb_filter_en, "config1:19"), + [cxl_pmu_crb_attr] = CXL_PMU_FORMAT_ATTR(crb, "config2:32-63"), NULL }; @@ -246,7 +257,9 @@ static struct attribute *cxl_pmu_format_attr[] = { #define CXL_PMU_ATTR_CONFIG1_INVERT_MSK BIT(16) #define CXL_PMU_ATTR_CONFIG1_EDGE_MSK BIT(17) #define CXL_PMU_ATTR_CONFIG1_FILTER_EN_MSK BIT(18) +#define CXL_PMU_ATTR_CONFIG1_CRB_FILTER_EN_MSK BIT(19) #define CXL_PMU_ATTR_CONFIG2_HDM_MSK GENMASK(15, 0) +#define CXL_PMU_ATTR_CONFIG2_CRB_MSK GENMASK_ULL(63, 32) static umode_t cxl_pmu_format_is_visible(struct kobject *kobj, struct attribute *attr, int a) @@ -263,6 +276,11 @@ static umode_t cxl_pmu_format_is_visible(struct kobject *kobj, attr == cxl_pmu_format_attr[cxl_pmu_hdm_attr])) return 0; + if (!info->filter_crb && + (attr == cxl_pmu_format_attr[cxl_pmu_crb_filter_en_attr] || + attr == cxl_pmu_format_attr[cxl_pmu_crb_attr])) + return 0; + return attr->mode; } @@ -319,6 +337,17 @@ static u16 cxl_pmu_config2_get_hdm_decoder(struct perf_event *event) return FIELD_GET(CXL_PMU_ATTR_CONFIG2_HDM_MSK, event->attr.config2); } +static u16 cxl_pmu_config1_crb_filter_en(struct perf_event *event) +{ + return FIELD_GET(CXL_PMU_ATTR_CONFIG1_CRB_FILTER_EN_MSK, + event->attr.config1); +} + +static u32 cxl_pmu_config2_get_crb(struct perf_event *event) +{ + return FIELD_GET(CXL_PMU_ATTR_CONFIG2_CRB_MSK, event->attr.config2); +} + static ssize_t cxl_pmu_event_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -385,13 +414,23 @@ static struct attribute *cxl_pmu_event_attrs[] = { CXL_PMU_EVENT_CXL_ATTR(m2s_req_memwrfwd, CXL_PMU_GID_M2S_REQ, BIT(4)), CXL_PMU_EVENT_CXL_ATTR(m2s_req_memrdtee, CXL_PMU_GID_M2S_REQ, BIT(5)), CXL_PMU_EVENT_CXL_ATTR(m2s_req_memrddatatee, CXL_PMU_GID_M2S_REQ, BIT(6)), + CXL_PMU_EVENT_CXL_ATTR(m2s_req_meminvtee, CXL_PMU_GID_M2S_REQ, BIT(7)), CXL_PMU_EVENT_CXL_ATTR(m2s_req_memspecrd, CXL_PMU_GID_M2S_REQ, BIT(8)), CXL_PMU_EVENT_CXL_ATTR(m2s_req_meminvnt, CXL_PMU_GID_M2S_REQ, BIT(9)), CXL_PMU_EVENT_CXL_ATTR(m2s_req_memcleanevict, CXL_PMU_GID_M2S_REQ, BIT(10)), + CXL_PMU_EVENT_CXL_ATTR(m2s_req_meminvptee, CXL_PMU_GID_M2S_REQ, BIT(11)), + CXL_PMU_EVENT_CXL_ATTR(m2s_req_memspecrdtee, CXL_PMU_GID_M2S_REQ, BIT(12)), + CXL_PMU_EVENT_CXL_ATTR(m2s_req_teupdate, CXL_PMU_GID_M2S_REQ, BIT(13)), + CXL_PMU_EVENT_CXL_ATTR(m2s_req_memclnevcttee, CXL_PMU_GID_M2S_REQ, BIT(14)), + CXL_PMU_EVENT_CXL_ATTR(m2s_req_memclnevctu, CXL_PMU_GID_M2S_REQ, BIT(15)), /* CXL rev 3.0 Table 3-35 M2S RwD Memory Opcodes */ CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_memwr, CXL_PMU_GID_M2S_RWD, BIT(1)), CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_memwrptl, CXL_PMU_GID_M2S_RWD, BIT(2)), CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_biconflict, CXL_PMU_GID_M2S_RWD, BIT(4)), + CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_memrdfill, CXL_PMU_GID_M2S_RWD, BIT(5)), + CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_memwrtee, CXL_PMU_GID_M2S_RWD, BIT(9)), + CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_memwrptltee, CXL_PMU_GID_M2S_RWD, BIT(10)), + CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_memrdfilltee, CXL_PMU_GID_M2S_RWD, BIT(13)), /* CXL rev 3.0 Table 3-38 M2S BIRsp Memory Opcodes */ CXL_PMU_EVENT_CXL_ATTR(m2s_birsp_i, CXL_PMU_GID_M2S_BIRSP, BIT(0)), CXL_PMU_EVENT_CXL_ATTR(m2s_birsp_s, CXL_PMU_GID_M2S_BIRSP, BIT(1)), @@ -406,15 +445,25 @@ static struct attribute *cxl_pmu_event_attrs[] = { CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_curblk, CXL_PMU_GID_S2M_BISNP, BIT(4)), CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_datblk, CXL_PMU_GID_S2M_BISNP, BIT(5)), CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_invblk, CXL_PMU_GID_S2M_BISNP, BIT(6)), + CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_curtee, CXL_PMU_GID_S2M_BISNP, BIT(8)), + CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_datatee, CXL_PMU_GID_S2M_BISNP, BIT(9)), + CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_invtee, CXL_PMU_GID_S2M_BISNP, BIT(10)), + CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_curblktee, CXL_PMU_GID_S2M_BISNP, BIT(12)), + CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_datablktee, CXL_PMU_GID_S2M_BISNP, BIT(13)), + CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_invblktee, CXL_PMU_GID_S2M_BISNP, BIT(14)), /* CXL rev 3.1 Table 3-50 S2M NDR Opcodes */ CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmp, CXL_PMU_GID_S2M_NDR, BIT(0)), CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmps, CXL_PMU_GID_S2M_NDR, BIT(1)), CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmpe, CXL_PMU_GID_S2M_NDR, BIT(2)), CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmpm, CXL_PMU_GID_S2M_NDR, BIT(3)), CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_biconflictack, CXL_PMU_GID_S2M_NDR, BIT(4)), + CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmptee, CXL_PMU_GID_S2M_NDR, BIT(5)), + CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmptee_s, CXL_PMU_GID_S2M_NDR, BIT(6)), + CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmptee_e, CXL_PMU_GID_S2M_NDR, BIT(7)), /* CXL rev 3.0 Table 3-46 S2M DRS opcodes */ CXL_PMU_EVENT_CXL_ATTR(s2m_drs_memdata, CXL_PMU_GID_S2M_DRS, BIT(0)), CXL_PMU_EVENT_CXL_ATTR(s2m_drs_memdatanxm, CXL_PMU_GID_S2M_DRS, BIT(1)), + CXL_PMU_EVENT_CXL_ATTR(s2m_drs_memdatatee, CXL_PMU_GID_S2M_DRS, BIT(2)), /* CXL rev 3.0 Table 13-5 directly lists these */ CXL_PMU_EVENT_CXL_ATTR(ddr_act, CXL_PMU_GID_DDR, BIT(0)), CXL_PMU_EVENT_CXL_ATTR(ddr_pre, CXL_PMU_GID_DDR, BIT(1)), @@ -423,6 +472,32 @@ static struct attribute *cxl_pmu_event_attrs[] = { CXL_PMU_EVENT_CXL_ATTR(ddr_refresh, CXL_PMU_GID_DDR, BIT(4)), CXL_PMU_EVENT_CXL_ATTR(ddr_selfrefreshent, CXL_PMU_GID_DDR, BIT(5)), CXL_PMU_EVENT_CXL_ATTR(ddr_rfm, CXL_PMU_GID_DDR, BIT(6)), + /* CXL 4.0 Table 13-5 DDR add-on events opcodes */ + CXL_PMU_EVENT_CXL_ATTR(ddr_cas_rd_ap, CXL_PMU_GID_DDR, BIT(7)), + CXL_PMU_EVENT_CXL_ATTR(ddr_cas_wr_ap, CXL_PMU_GID_DDR, BIT(8)), + CXL_PMU_EVENT_CXL_ATTR(ddr_refresh_all_banks, CXL_PMU_GID_DDR, BIT(9)), + CXL_PMU_EVENT_CXL_ATTR(ddr_refresh_same_bank, CXL_PMU_GID_DDR, BIT(10)), + CXL_PMU_EVENT_CXL_ATTR(ddr_pwrdn_entry, CXL_PMU_GID_DDR, BIT(11)), + CXL_PMU_EVENT_CXL_ATTR(ddr_pwrdn_exit, CXL_PMU_GID_DDR, BIT(12)), + CXL_PMU_EVENT_CXL_ATTR(ddr_rd_wr_ddr_bus_switching, CXL_PMU_GID_DDR, BIT(13)), + CXL_PMU_EVENT_CXL_ATTR(ddr_incoming_rd_req, CXL_PMU_GID_DDR, BIT(14)), + CXL_PMU_EVENT_CXL_ATTR(ddr_incoming_wr_req, CXL_PMU_GID_DDR, BIT(15)), + /* CXL 4.0 Table 13-5 QUEUE OCCUPANCY events opcodes */ + CXL_PMU_EVENT_CXL_ATTR(rd_queue_occ, CXL_PMU_GID_QUEUE_OCC, BIT(0)), + CXL_PMU_EVENT_CXL_ATTR(wr_queue_occ, CXL_PMU_GID_QUEUE_OCC, BIT(1)), + CXL_PMU_EVENT_CXL_ATTR(rd_wr_merged_queue_occ, CXL_PMU_GID_QUEUE_OCC, BIT(2)), + CXL_PMU_EVENT_CXL_ATTR(pwrdn_event, CXL_PMU_GID_QUEUE_OCC, BIT(3)), + /* CXL 4.0 Table 13-5 QUEUE RESIDENCY events opcodes */ + CXL_PMU_EVENT_CXL_ATTR(mc_rd_resid_cnt, CXL_PMU_GID_QUEUE_RESID, BIT(0)), + CXL_PMU_EVENT_CXL_ATTR(mc_wr_resid_cnt, CXL_PMU_GID_QUEUE_RESID, BIT(1)), + /* CXL 4.0 Table 13-5 RETRY events opcodes */ + CXL_PMU_EVENT_CXL_ATTR(retry_event_trig_by_rd_crc, CXL_PMU_GID_RETRY_EVENTS, BIT(0)), + CXL_PMU_EVENT_CXL_ATTR(retry_event_trig_by_wr_crc, CXL_PMU_GID_RETRY_EVENTS, BIT(1)), + CXL_PMU_EVENT_CXL_ATTR(retry_event_trig_by_ca_parity, CXL_PMU_GID_RETRY_EVENTS, BIT(2)), + CXL_PMU_EVENT_CXL_ATTR(retry_event_trig_by_ecc, CXL_PMU_GID_RETRY_EVENTS, BIT(3)), + /* CXL 4.0 Table 13-5 THROTTLE events opcodes */ + CXL_PMU_EVENT_CXL_ATTR(thermal_throttle_event, CXL_PMU_GID_THROTTLE, BIT(0)), + CXL_PMU_EVENT_CXL_ATTR(power_throttle_event, CXL_PMU_GID_THROTTLE, BIT(1)), NULL }; @@ -571,6 +646,36 @@ static int cxl_pmu_event_init(struct perf_event *event) return -EOPNOTSUPP; /* TODO: Validation of any filter */ + if (cxl_pmu_config1_crb_filter_en(event)) { + if (!info->filter_crb) + return -EINVAL; + /* event group IDs are scoped by the CXL vendor ID */ + if (cxl_pmu_config_get_vid(event) != PCI_VENDOR_ID_CXL) + return -EINVAL; + + /* + * CRB filtering (Filter ID 1) is only valid for the DDR + * Interface, Queue Occupancy, Queue Residency and Retry + * event groups (CXL 4.0 Table 13-5). + */ + switch (cxl_pmu_config_get_gid(event)) { + case CXL_PMU_GID_DDR: + case CXL_PMU_GID_QUEUE_OCC: + case CXL_PMU_GID_QUEUE_RESID: + case CXL_PMU_GID_RETRY_EVENTS: + break; + default: + return -EINVAL; + } + + /* + * Filtering while counting multiple events is + * undefined behavior. + */ + if (hweight32(cxl_pmu_config_get_mask(event)) > 1) + return -EINVAL; + } + /* * Verify that it is possible to count what was requested. Either must * be a fixed counter that is a precise match or a configurable counter @@ -627,15 +732,23 @@ static void cxl_pmu_event_start(struct perf_event *event, int flags) hwc->state = 0; /* - * Currently only hdm filter control is implemented, this code will - * want generalizing when more filters are added. + * Filter ID=0: HDM decoder filter + * Filter ID=1: Channel/Rank/Bank (CRB) filter */ if (info->filter_hdm) { if (cxl_pmu_config1_hdm_filter_en(event)) cfg = cxl_pmu_config2_get_hdm_decoder(event); else cfg = GENMASK(31, 0); /* No filtering if 0xFFFF_FFFF */ - writeq(cfg, base + CXL_PMU_FILTER_CFG_REG(hwc->idx, 0)); + writel(cfg, base + CXL_PMU_FILTER_CFG_REG(hwc->idx, 0)); + } + + if (info->filter_crb) { + if (cxl_pmu_config1_crb_filter_en(event)) + cfg = cxl_pmu_config2_get_crb(event); + else + cfg = GENMASK(31, 0); /* no filtering if 0xFFFF_FFFF */ + writel(cfg, base + CXL_PMU_FILTER_CFG_REG(hwc->idx, 1)); } cfg = readq(base + CXL_PMU_COUNTER_CFG_REG(hwc->idx)); diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c index 5385401fa9cf..f5f3e5b83f5b 100644 --- a/drivers/perf/dwc_pcie_pmu.c +++ b/drivers/perf/dwc_pcie_pmu.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +84,7 @@ enum dwc_pcie_event_type { #define DWC_PCIE_LANE_EVENT_MAX_PERIOD GENMASK_ULL(31, 0) #define DWC_PCIE_MAX_PERIOD GENMASK_ULL(63, 0) +#define DWC_PCIE_PMU_TIMER_PERIOD_NS (2 * NSEC_PER_SEC) struct dwc_pcie_pmu { struct pmu pmu; @@ -93,6 +95,8 @@ struct dwc_pcie_pmu { /* Groups #6 and #7 */ DECLARE_BITMAP(lane_events, 2 * DWC_PCIE_LANE_MAX_EVENTS_PER_GROUP); struct perf_event *time_based_event; + bool timer_enable; + struct hrtimer hrtimer; struct hlist_node cpuhp_node; int on_cpu; @@ -354,6 +358,26 @@ static u64 dwc_pcie_pmu_read_time_based_counter(struct perf_event *event) return val; } +static void dwc_pcie_pmu_reset_time_based_counter(struct perf_event *event) +{ + struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + u64 prev; + + dwc_pcie_pmu_time_based_event_enable(pcie_pmu, false); + + /* + * The hardware counter is reset to zero when disabled. Synchronize + * prev_count so that the next event_update() computes the correct + * delta against the new counter baseline. + */ + do { + prev = local64_read(&hwc->prev_count); + } while (local64_cmpxchg(&hwc->prev_count, prev, 0) != prev); + + dwc_pcie_pmu_time_based_event_enable(pcie_pmu, true); +} + static void dwc_pcie_pmu_event_update(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; @@ -429,6 +453,26 @@ static int dwc_pcie_pmu_validate_group(struct perf_event *event) return 0; } +static enum hrtimer_restart dwc_pcie_pmu_hrtimer_callback(struct hrtimer *hrtimer) +{ + struct dwc_pcie_pmu *pcie_pmu = container_of(hrtimer, struct dwc_pcie_pmu, hrtimer); + struct perf_event *event = pcie_pmu->time_based_event; + struct hw_perf_event *hwc; + + if (!event) + return HRTIMER_NORESTART; + + hwc = &event->hw; + if (hwc->state & PERF_HES_STOPPED) + return HRTIMER_NORESTART; + + dwc_pcie_pmu_event_update(event); + dwc_pcie_pmu_reset_time_based_counter(event); + hrtimer_forward_now(hrtimer, ns_to_ktime(DWC_PCIE_PMU_TIMER_PERIOD_NS)); + + return HRTIMER_RESTART; +} + static int dwc_pcie_pmu_event_init(struct perf_event *event) { struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu); @@ -478,10 +522,15 @@ static void dwc_pcie_pmu_event_start(struct perf_event *event, int flags) hwc->state = 0; local64_set(&hwc->prev_count, 0); - if (type == DWC_PCIE_LANE_EVENT) + if (type == DWC_PCIE_LANE_EVENT) { dwc_pcie_pmu_lane_event_enable(pcie_pmu, event, true); - else if (type == DWC_PCIE_TIME_BASE_EVENT) + } else if (type == DWC_PCIE_TIME_BASE_EVENT) { dwc_pcie_pmu_time_based_event_enable(pcie_pmu, true); + if (pcie_pmu->timer_enable) + hrtimer_start(&pcie_pmu->hrtimer, + ns_to_ktime(DWC_PCIE_PMU_TIMER_PERIOD_NS), + HRTIMER_MODE_REL_PINNED_HARD); + } } static void dwc_pcie_pmu_event_stop(struct perf_event *event, int flags) @@ -495,11 +544,15 @@ static void dwc_pcie_pmu_event_stop(struct perf_event *event, int flags) dwc_pcie_pmu_event_update(event); - if (type == DWC_PCIE_LANE_EVENT) + if (type == DWC_PCIE_LANE_EVENT) { dwc_pcie_pmu_lane_event_enable(pcie_pmu, event, false); - else if (type == DWC_PCIE_TIME_BASE_EVENT) + } else if (type == DWC_PCIE_TIME_BASE_EVENT) { dwc_pcie_pmu_time_based_event_enable(pcie_pmu, false); + if (pcie_pmu->timer_enable) + hrtimer_cancel(&pcie_pmu->hrtimer); + } + hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; } @@ -726,6 +779,16 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev) pcie_pmu->ras_des_offset = vsec; pcie_pmu->nr_lanes = pcie_get_width_cap(pdev); pcie_pmu->on_cpu = -1; + hrtimer_setup(&pcie_pmu->hrtimer, dwc_pcie_pmu_hrtimer_callback, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED_HARD); + + /* + * Use timer for updating time-based counts on platforms known + * to have narrowed counter. + */ + if (pdev->vendor == PCI_VENDOR_ID_PICOHEART) + pcie_pmu->timer_enable = true; + pcie_pmu->pmu = (struct pmu){ .name = name, .parent = &plat_dev->dev, diff --git a/drivers/perf/fsl_imx8_ddr_perf.c b/drivers/perf/fsl_imx8_ddr_perf.c index bcdf5575d71c..6d1e99abf110 100644 --- a/drivers/perf/fsl_imx8_ddr_perf.c +++ b/drivers/perf/fsl_imx8_ddr_perf.c @@ -858,10 +858,8 @@ static int ddr_perf_probe(struct platform_device *pdev) IRQF_NOBALANCING | IRQF_NO_THREAD, DDR_CPUHP_CB_NAME, pmu); - if (ret < 0) { - dev_err(&pdev->dev, "Request irq failed: %d", ret); + if (ret < 0) goto ddr_perf_err; - } pmu->irq = irq; ret = irq_set_affinity(pmu->irq, cpumask_of(pmu->cpu)); diff --git a/drivers/perf/fsl_imx9_ddr_perf.c b/drivers/perf/fsl_imx9_ddr_perf.c index 7050b48c0467..9dbc3187ef52 100644 --- a/drivers/perf/fsl_imx9_ddr_perf.c +++ b/drivers/perf/fsl_imx9_ddr_perf.c @@ -830,10 +830,8 @@ static int ddr_perf_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, ddr_perf_irq_handler, IRQF_NOBALANCING | IRQF_NO_THREAD, DDR_CPUHP_CB_NAME, pmu); - if (ret < 0) { - dev_err(&pdev->dev, "Request irq failed: %d", ret); + if (ret < 0) goto ddr_perf_err; - } pmu->irq = irq; ret = irq_set_affinity(pmu->irq, cpumask_of(pmu->cpu)); diff --git a/drivers/perf/fujitsu_uncore_pmu.c b/drivers/perf/fujitsu_uncore_pmu.c index aeeb68c66e1e..8c87d91e64b5 100644 --- a/drivers/perf/fujitsu_uncore_pmu.c +++ b/drivers/perf/fujitsu_uncore_pmu.c @@ -526,7 +526,7 @@ static int fujitsu_uncore_pmu_probe(struct platform_device *pdev) IRQF_NOBALANCING | IRQF_NO_THREAD, name, uncorepmu); if (ret) - return dev_err_probe(dev, ret, "Failed to request IRQ:%d\n", irq); + return ret; ret = irq_set_affinity(irq, cpumask_of(uncorepmu->cpu)); if (ret) diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index f963e4f9e552..56a88fb0d3c2 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -604,8 +604,7 @@ static int hisi_l3c_pmu_init_ext(struct hisi_pmu *l3c_pmu, struct platform_devic IRQF_NOBALANCING | IRQF_NO_THREAD, irqname, l3c_pmu); if (ret < 0) - return dev_err_probe(&pdev->dev, ret, - "Fail to request EXT IRQ: %d.\n", irq); + return ret; hisi_l3c_pmu->ext_irq[i] = irq; } diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index de71dcf11653..f6f5b4470efe 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -192,11 +192,8 @@ int hisi_uncore_pmu_init_irq(struct hisi_pmu *hisi_pmu, ret = devm_request_irq(&pdev->dev, irq, hisi_uncore_pmu_isr, IRQF_NOBALANCING | IRQF_NO_THREAD, dev_name(&pdev->dev), hisi_pmu); - if (ret < 0) { - dev_err(&pdev->dev, - "Fail to request IRQ: %d ret: %d.\n", irq, ret); + if (ret < 0) return ret; - } hisi_pmu->irq = irq; diff --git a/drivers/perf/marvell_cn10k_ddr_pmu.c b/drivers/perf/marvell_cn10k_ddr_pmu.c index 72ac17efd846..9c82bf4ee2c4 100644 --- a/drivers/perf/marvell_cn10k_ddr_pmu.c +++ b/drivers/perf/marvell_cn10k_ddr_pmu.c @@ -13,31 +13,43 @@ #include #include #include +#include + +/* SoC variant flags for struct ddr_pmu_platform_data (mutually exclusive in pdata) */ +#define IS_CN10K BIT(0) +#define IS_ODY BIT(1) +#define IS_CN20K BIT(2) /* Performance Counters Operating Mode Control Registers */ #define CN10K_DDRC_PERF_CNT_OP_MODE_CTRL 0x8020 #define ODY_DDRC_PERF_CNT_OP_MODE_CTRL 0x20020 +#define CN20K_DDRC_PERF_CNT_OP_MODE_CTRL 0x20000 #define OP_MODE_CTRL_VAL_MANUAL 0x1 /* Performance Counters Start Operation Control Registers */ #define CN10K_DDRC_PERF_CNT_START_OP_CTRL 0x8028 #define ODY_DDRC_PERF_CNT_START_OP_CTRL 0x200A0 +#define CN20K_DDRC_PERF_CNT_START_OP_CTRL 0x20080 #define START_OP_CTRL_VAL_START 0x1ULL #define START_OP_CTRL_VAL_ACTIVE 0x2 /* Performance Counters End Operation Control Registers */ #define CN10K_DDRC_PERF_CNT_END_OP_CTRL 0x8030 #define ODY_DDRC_PERF_CNT_END_OP_CTRL 0x200E0 +#define CN20K_DDRC_PERF_CNT_END_OP_CTRL 0x200C0 #define END_OP_CTRL_VAL_END 0x1ULL /* Performance Counters End Status Registers */ #define CN10K_DDRC_PERF_CNT_END_STATUS 0x8038 #define ODY_DDRC_PERF_CNT_END_STATUS 0x20120 +#define CN20K_DDRC_PERF_CNT_END_STATUS 0x20100 #define END_STATUS_VAL_END_TIMER_MODE_END 0x1 /* Performance Counters Configuration Registers */ #define CN10K_DDRC_PERF_CFG_BASE 0x8040 #define ODY_DDRC_PERF_CFG_BASE 0x20160 +#define CN20K_DDRC_PERF_CFG_BASE 0x20140 +#define CN20K_DDRC_PERF_CFG1_BASE 0x20180 /* 8 Generic event counter + 2 fixed event counters */ #define DDRC_PERF_NUM_GEN_COUNTERS 8 @@ -61,6 +73,24 @@ * DO NOT change these event-id numbers, they are used to * program event bitmap in h/w. */ + +/* CN20K specific events */ +#define EVENT_PERF_OP_IS_RD16 61 +#define EVENT_PERF_OP_IS_RD32 60 +#define EVENT_PERF_OP_IS_WR16 59 +#define EVENT_PERF_OP_IS_WR32 58 +#define EVENT_OP_IS_ENTER_DSM 44 +#define EVENT_OP_IS_RFM 43 + + +#define EVENT_CN20K_OP_IS_ZQLATCH 62 +#define EVENT_CN20K_OP_IS_ZQSTART 63 +#define EVENT_CN20K_OP_IS_TCR_MRR 50 +#define EVENT_CN20K_OP_IS_DQSOSC_MRR 49 +#define EVENT_CN20K_OP_IS_DQSOSC_MPC 48 +#define EVENT_CN20K_VISIBLE_WIN_LIMIT_REACHED_WR 47 +#define EVENT_CN20K_VISIBLE_WIN_LIMIT_REACHED_RD 46 + #define EVENT_DFI_CMD_IS_RETRY 61 #define EVENT_RD_UC_ECC_ERROR 60 #define EVENT_RD_CRC_ERROR 59 @@ -87,6 +117,9 @@ #define EVENT_OP_IS_SPEC_REF 41 #define EVENT_OP_IS_CRIT_REF 40 #define EVENT_OP_IS_REFRESH 39 +#define EVENT_OP_IS_CAS_WCK_SUS 38 +#define EVENT_OP_IS_CAS_WS_OFF 37 +#define EVENT_OP_IS_CAS_WS 36 #define EVENT_OP_IS_ENTER_MPSM 35 #define EVENT_OP_IS_ENTER_POWERDOWN 31 #define EVENT_OP_IS_ENTER_SELFREF 27 @@ -183,8 +216,8 @@ struct ddr_pmu_platform_data { u64 cnt_freerun_clr; u64 cnt_value_wr_op; u64 cnt_value_rd_op; - bool is_cn10k; - bool is_ody; + u64 cfg1_base; + unsigned int silicon_flags; /* IS_CN10K, IS_ODY, or IS_CN20K */ }; static ssize_t cn10k_ddr_pmu_event_show(struct device *dev, @@ -336,6 +369,80 @@ static struct attribute *odyssey_ddr_perf_events_attrs[] = { NULL }; +static struct attribute *cn20k_ddr_perf_events_attrs[] = { + /* Programmable */ + CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_rd_or_wr_access, EVENT_HIF_RD_OR_WR), + CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_wr_access, EVENT_HIF_WR), + CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_rd_access, EVENT_HIF_RD), + CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_rmw_access, EVENT_HIF_RMW), + CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_pri_rdaccess, EVENT_HIF_HI_PRI_RD), + CN10K_DDR_PMU_EVENT_ATTR(ddr_rd_bypass_access, EVENT_READ_BYPASS), + CN10K_DDR_PMU_EVENT_ATTR(ddr_act_bypass_access, EVENT_ACT_BYPASS), + CN10K_DDR_PMU_EVENT_ATTR(ddr_dfi_wr_data_access, + EVENT_DFI_WR_DATA_CYCLES), + CN10K_DDR_PMU_EVENT_ATTR(ddr_dfi_rd_data_access, + EVENT_DFI_RD_DATA_CYCLES), + CN10K_DDR_PMU_EVENT_ATTR(ddr_hpri_sched_rd_crit_access, + EVENT_HPR_XACT_WHEN_CRITICAL), + CN10K_DDR_PMU_EVENT_ATTR(ddr_lpri_sched_rd_crit_access, + EVENT_LPR_XACT_WHEN_CRITICAL), + CN10K_DDR_PMU_EVENT_ATTR(ddr_wr_trxn_crit_access, + EVENT_WR_XACT_WHEN_CRITICAL), + CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_active_access, EVENT_OP_IS_ACTIVATE), + CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_rd_or_wr_access, + EVENT_OP_IS_RD_OR_WR), + CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_rd_active_access, + EVENT_OP_IS_RD_ACTIVATE), + CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_read, EVENT_OP_IS_RD), + CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_write, EVENT_OP_IS_WR), + CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_mwr, EVENT_OP_IS_MWR), + CN10K_DDR_PMU_EVENT_ATTR(ddr_precharge, EVENT_OP_IS_PRECHARGE), + CN10K_DDR_PMU_EVENT_ATTR(ddr_precharge_for_rdwr, + EVENT_PRECHARGE_FOR_RDWR), + CN10K_DDR_PMU_EVENT_ATTR(ddr_precharge_for_other, + EVENT_PRECHARGE_FOR_OTHER), + CN10K_DDR_PMU_EVENT_ATTR(ddr_rdwr_transitions, EVENT_RDWR_TRANSITIONS), + CN10K_DDR_PMU_EVENT_ATTR(ddr_write_combine, EVENT_WRITE_COMBINE), + CN10K_DDR_PMU_EVENT_ATTR(ddr_war_hazard, EVENT_WAR_HAZARD), + CN10K_DDR_PMU_EVENT_ATTR(ddr_raw_hazard, EVENT_RAW_HAZARD), + CN10K_DDR_PMU_EVENT_ATTR(ddr_waw_hazard, EVENT_WAW_HAZARD), + CN10K_DDR_PMU_EVENT_ATTR(ddr_enter_selfref, EVENT_OP_IS_ENTER_SELFREF), + CN10K_DDR_PMU_EVENT_ATTR(ddr_enter_powerdown, + EVENT_OP_IS_ENTER_POWERDOWN), + CN10K_DDR_PMU_EVENT_ATTR(ddr_cas_ws, EVENT_OP_IS_CAS_WS), + CN10K_DDR_PMU_EVENT_ATTR(ddr_cas_ws_off, EVENT_OP_IS_CAS_WS_OFF), + CN10K_DDR_PMU_EVENT_ATTR(ddr_cas_wck_sus, EVENT_OP_IS_CAS_WCK_SUS), + CN10K_DDR_PMU_EVENT_ATTR(ddr_refresh, EVENT_OP_IS_REFRESH), + CN10K_DDR_PMU_EVENT_ATTR(ddr_crit_ref, EVENT_OP_IS_CRIT_REF), + CN10K_DDR_PMU_EVENT_ATTR(ddr_spec_ref, EVENT_OP_IS_SPEC_REF), + CN10K_DDR_PMU_EVENT_ATTR(ddr_load_mode, EVENT_OP_IS_LOAD_MODE), + CN10K_DDR_PMU_EVENT_ATTR(ddr_rfm, EVENT_OP_IS_RFM), + CN10K_DDR_PMU_EVENT_ATTR(ddr_enter_dsm, EVENT_OP_IS_ENTER_DSM), + CN10K_DDR_PMU_EVENT_ATTR(ddr_dfi_cycles, EVENT_DFI_CYCLES), + CN10K_DDR_PMU_EVENT_ATTR(ddr_win_limit_reached_rd, + EVENT_CN20K_VISIBLE_WIN_LIMIT_REACHED_RD), + CN10K_DDR_PMU_EVENT_ATTR(ddr_win_limit_reached_wr, + EVENT_CN20K_VISIBLE_WIN_LIMIT_REACHED_WR), + CN10K_DDR_PMU_EVENT_ATTR(ddr_dqsosc_mpc, EVENT_CN20K_OP_IS_DQSOSC_MPC), + CN10K_DDR_PMU_EVENT_ATTR(ddr_dqsosc_mrr, EVENT_CN20K_OP_IS_DQSOSC_MRR), + CN10K_DDR_PMU_EVENT_ATTR(ddr_tcr_mrr, EVENT_CN20K_OP_IS_TCR_MRR), + CN10K_DDR_PMU_EVENT_ATTR(ddr_zqstart, EVENT_CN20K_OP_IS_ZQSTART), + CN10K_DDR_PMU_EVENT_ATTR(ddr_zqlatch, EVENT_CN20K_OP_IS_ZQLATCH), + CN10K_DDR_PMU_EVENT_ATTR(ddr_read16, EVENT_PERF_OP_IS_RD16), + CN10K_DDR_PMU_EVENT_ATTR(ddr_read32, EVENT_PERF_OP_IS_RD32), + CN10K_DDR_PMU_EVENT_ATTR(ddr_write16, EVENT_PERF_OP_IS_WR16), + CN10K_DDR_PMU_EVENT_ATTR(ddr_write32, EVENT_PERF_OP_IS_WR32), + /* Free run event counters */ + CN10K_DDR_PMU_EVENT_ATTR(ddr_ddr_reads, EVENT_DDR_READS), + CN10K_DDR_PMU_EVENT_ATTR(ddr_ddr_writes, EVENT_DDR_WRITES), + NULL +}; + +static struct attribute_group cn20k_ddr_perf_events_attr_group = { + .name = "events", + .attrs = cn20k_ddr_perf_events_attrs, +}; + static struct attribute_group odyssey_ddr_perf_events_attr_group = { .name = "events", .attrs = odyssey_ddr_perf_events_attrs, @@ -393,6 +500,13 @@ static const struct attribute_group *odyssey_attr_groups[] = { NULL }; +static const struct attribute_group *cn20k_attr_groups[] = { + &cn20k_ddr_perf_events_attr_group, + &cn10k_ddr_perf_format_attr_group, + &cn10k_ddr_perf_cpumask_attr_group, + NULL +}; + /* Default poll timeout is 100 sec, which is very sufficient for * 48 bit counter incremented max at 5.6 GT/s, which may take many * hours to overflow. @@ -411,14 +525,38 @@ static int ddr_perf_get_event_bitmap(int eventid, u64 *event_bitmap, int err = 0; switch (eventid) { + case EVENT_CN20K_OP_IS_ZQLATCH ... EVENT_CN20K_OP_IS_ZQSTART: + if (ddr_pmu->p_data->silicon_flags & IS_CN20K) { + *event_bitmap = (1ULL << (eventid - 42)); + break; + } + err = -EINVAL; + break; case EVENT_DFI_PARITY_POISON ...EVENT_DFI_CMD_IS_RETRY: - if (!ddr_pmu->p_data->is_ody) { + /* + * 58..61: CN20K perf width events share numeric IDs with Odyssey + * DFI events; same 1ULL << (eventid - 1) bitmap on both paths. + */ + if (eventid >= EVENT_PERF_OP_IS_WR32 && + eventid <= EVENT_PERF_OP_IS_RD16) { + if (ddr_pmu->p_data->silicon_flags & IS_CN20K) { + *event_bitmap = (1ULL << (eventid - 1)); + break; + } + if (!(ddr_pmu->p_data->silicon_flags & IS_ODY)) { + err = -EINVAL; + break; + } + *event_bitmap = (1ULL << (eventid - 1)); + break; + } + if (!(ddr_pmu->p_data->silicon_flags & IS_ODY)) { err = -EINVAL; break; } fallthrough; case EVENT_HIF_RD_OR_WR ... EVENT_WAW_HAZARD: - case EVENT_OP_IS_REFRESH ... EVENT_OP_IS_ZQLATCH: + case EVENT_OP_IS_CAS_WS ... EVENT_OP_IS_ZQLATCH: *event_bitmap = (1ULL << (eventid - 1)); break; case EVENT_OP_IS_ENTER_SELFREF: @@ -524,9 +662,9 @@ static void cn10k_ddr_perf_counter_enable(struct cn10k_ddr_pmu *pmu, int counter, bool enable) { const struct ddr_pmu_platform_data *p_data = pmu->p_data; + unsigned int silicon_flags = pmu->p_data->silicon_flags; u64 ctrl_reg = pmu->p_data->cnt_op_mode_ctrl; const struct ddr_pmu_ops *ops = pmu->ops; - bool is_ody = pmu->p_data->is_ody; u32 reg; u64 val; @@ -546,7 +684,7 @@ static void cn10k_ddr_perf_counter_enable(struct cn10k_ddr_pmu *pmu, writeq_relaxed(val, pmu->base + reg); - if (is_ody) { + if ((silicon_flags & IS_ODY) || (silicon_flags & IS_CN20K)) { if (enable) { /* * Setup the PMU counter to work in @@ -621,6 +759,7 @@ static int cn10k_ddr_perf_event_add(struct perf_event *event, int flags) { struct cn10k_ddr_pmu *pmu = to_cn10k_ddr_pmu(event->pmu); const struct ddr_pmu_platform_data *p_data = pmu->p_data; + unsigned int silicon_flags = pmu->p_data->silicon_flags; const struct ddr_pmu_ops *ops = pmu->ops; struct hw_perf_event *hwc = &event->hw; u8 config = event->attr.config; @@ -642,10 +781,27 @@ static int cn10k_ddr_perf_event_add(struct perf_event *event, int flags) if (counter < DDRC_PERF_NUM_GEN_COUNTERS) { /* Generic counters, configure event id */ reg_offset = DDRC_PERF_CFG(p_data->cfg_base, counter); + ret = ddr_perf_get_event_bitmap(config, &val, pmu); if (ret) - return ret; + goto err_free_counter; + if (silicon_flags & IS_CN20K) { + if (config == EVENT_CN20K_OP_IS_ZQSTART || + config == EVENT_CN20K_OP_IS_ZQLATCH) { + /* ZQ lives in CFG1; clear stale event mask in CFG0 */ + writeq_relaxed(0, pmu->base + + DDRC_PERF_CFG(p_data->cfg_base, + counter)); + reg_offset = DDRC_PERF_CFG(p_data->cfg1_base, + counter); + } else { + /* Clear CFG1 so a prior ZQ select cannot linger */ + writeq_relaxed(0, pmu->base + + DDRC_PERF_CFG(p_data->cfg1_base, + counter)); + } + } writeq_relaxed(val, pmu->base + reg_offset); } else { /* fixed event counter, clear counter value */ @@ -661,6 +817,14 @@ static int cn10k_ddr_perf_event_add(struct perf_event *event, int flags) cn10k_ddr_perf_event_start(event, flags); return 0; + +err_free_counter: + if (pmu->active_events == 1) + hrtimer_cancel(&pmu->hrtimer); + pmu->active_events--; + cn10k_ddr_perf_free_counter(pmu, counter); + hwc->idx = -1; + return ret; } static void cn10k_ddr_perf_event_stop(struct perf_event *event, int flags) @@ -952,7 +1116,25 @@ static const struct ddr_pmu_platform_data cn10k_ddr_pmu_pdata = { .cnt_freerun_clr = 0, .cnt_value_wr_op = CN10K_DDRC_PERF_CNT_VALUE_WR_OP, .cnt_value_rd_op = CN10K_DDRC_PERF_CNT_VALUE_RD_OP, - .is_cn10k = TRUE, + .silicon_flags = IS_CN10K, +}; + +static const struct ddr_pmu_platform_data cn20k_ddr_pmu_pdata = { + .counter_overflow_val = 0, + .counter_max_val = GENMASK_ULL(63, 0), + .cnt_base = ODY_DDRC_PERF_CNT_VALUE_BASE, + .cfg_base = CN20K_DDRC_PERF_CFG_BASE, + .cfg1_base = CN20K_DDRC_PERF_CFG1_BASE, + .cnt_op_mode_ctrl = CN20K_DDRC_PERF_CNT_OP_MODE_CTRL, + .cnt_start_op_ctrl = CN20K_DDRC_PERF_CNT_START_OP_CTRL, + .cnt_end_op_ctrl = CN20K_DDRC_PERF_CNT_END_OP_CTRL, + .cnt_end_status = CN20K_DDRC_PERF_CNT_END_STATUS, + .cnt_freerun_en = 0, + .cnt_freerun_ctrl = ODY_DDRC_PERF_CNT_FREERUN_CTRL, + .cnt_freerun_clr = ODY_DDRC_PERF_CNT_FREERUN_CLR, + .cnt_value_wr_op = ODY_DDRC_PERF_CNT_VALUE_WR_OP, + .cnt_value_rd_op = ODY_DDRC_PERF_CNT_VALUE_RD_OP, + .silicon_flags = IS_CN20K, }; #endif @@ -979,7 +1161,7 @@ static const struct ddr_pmu_platform_data odyssey_ddr_pmu_pdata = { .cnt_freerun_clr = ODY_DDRC_PERF_CNT_FREERUN_CLR, .cnt_value_wr_op = ODY_DDRC_PERF_CNT_VALUE_WR_OP, .cnt_value_rd_op = ODY_DDRC_PERF_CNT_VALUE_RD_OP, - .is_ody = TRUE, + .silicon_flags = IS_ODY, }; #endif @@ -989,8 +1171,7 @@ static int cn10k_ddr_perf_probe(struct platform_device *pdev) struct cn10k_ddr_pmu *ddr_pmu; struct resource *res; void __iomem *base; - bool is_cn10k; - bool is_ody; + unsigned int silicon_flags; char *name; int ret; @@ -1014,10 +1195,9 @@ static int cn10k_ddr_perf_probe(struct platform_device *pdev) ddr_pmu->base = base; ddr_pmu->p_data = dev_data; - is_cn10k = ddr_pmu->p_data->is_cn10k; - is_ody = ddr_pmu->p_data->is_ody; + silicon_flags = ddr_pmu->p_data->silicon_flags; - if (is_cn10k) { + if (silicon_flags & IS_CN10K) { ddr_pmu->ops = &ddr_pmu_ops; /* Setup the PMU counter to work in manual mode */ writeq_relaxed(OP_MODE_CTRL_VAL_MANUAL, ddr_pmu->base + @@ -1039,7 +1219,7 @@ static int cn10k_ddr_perf_probe(struct platform_device *pdev) }; } - if (is_ody) { + if (silicon_flags & IS_ODY) { ddr_pmu->ops = &ddr_pmu_ody_ops; ddr_pmu->pmu = (struct pmu) { @@ -1056,6 +1236,22 @@ static int cn10k_ddr_perf_probe(struct platform_device *pdev) }; } + if (silicon_flags & IS_CN20K) { + ddr_pmu->ops = &ddr_pmu_ody_ops; + + ddr_pmu->pmu = (struct pmu) { + .module = THIS_MODULE, + .capabilities = PERF_PMU_CAP_NO_EXCLUDE, + .task_ctx_nr = perf_invalid_context, + .attr_groups = cn20k_attr_groups, + .event_init = cn10k_ddr_perf_event_init, + .add = cn10k_ddr_perf_event_add, + .del = cn10k_ddr_perf_event_del, + .start = cn10k_ddr_perf_event_start, + .stop = cn10k_ddr_perf_event_stop, + .read = cn10k_ddr_perf_event_update, + }; + } /* Choose this cpu to collect perf data */ ddr_pmu->cpu = raw_smp_processor_id(); @@ -1088,6 +1284,12 @@ static void cn10k_ddr_perf_remove(struct platform_device *pdev) { struct cn10k_ddr_pmu *ddr_pmu = platform_get_drvdata(pdev); + /* + * Cancel the poll timer before further teardown so the handler + * cannot run after this function returns. + */ + hrtimer_cancel(&ddr_pmu->hrtimer); + cpuhp_state_remove_instance_nocalls( CPUHP_AP_PERF_ARM_MARVELL_CN10K_DDR_ONLINE, &ddr_pmu->node); @@ -1098,6 +1300,7 @@ static void cn10k_ddr_perf_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id cn10k_ddr_pmu_of_match[] = { { .compatible = "marvell,cn10k-ddr-pmu", .data = &cn10k_ddr_pmu_pdata }, + { .compatible = "marvell,cn20k-ddr-pmu", .data = &cn20k_ddr_pmu_pdata }, { }, }; MODULE_DEVICE_TABLE(of, cn10k_ddr_pmu_of_match); @@ -1107,6 +1310,7 @@ MODULE_DEVICE_TABLE(of, cn10k_ddr_pmu_of_match); static const struct acpi_device_id cn10k_ddr_pmu_acpi_match[] = { {"MRVL000A", (kernel_ulong_t)&cn10k_ddr_pmu_pdata }, {"MRVL000C", (kernel_ulong_t)&odyssey_ddr_pmu_pdata}, + {"MRVL000B", (kernel_ulong_t)&cn20k_ddr_pmu_pdata}, {}, }; MODULE_DEVICE_TABLE(acpi, cn10k_ddr_pmu_acpi_match); diff --git a/drivers/perf/qcom_l2_pmu.c b/drivers/perf/qcom_l2_pmu.c index ea8c85729937..57a28ddb9b0e 100644 --- a/drivers/perf/qcom_l2_pmu.c +++ b/drivers/perf/qcom_l2_pmu.c @@ -869,11 +869,8 @@ static int l2_cache_pmu_probe_cluster(struct device *dev, void *data) IRQF_NOBALANCING | IRQF_NO_THREAD | IRQF_NO_AUTOEN, "l2-cache-pmu", cluster); - if (err) { - dev_err(&pdev->dev, - "Unable to request IRQ%d for L2 PMU counters\n", irq); + if (err) return err; - } dev_info(&pdev->dev, "Registered L2 cache PMU cluster %lld\n", fw_cluster_id); diff --git a/drivers/perf/qcom_l3_pmu.c b/drivers/perf/qcom_l3_pmu.c index 66e6cabd6fff..5897ec5560fc 100644 --- a/drivers/perf/qcom_l3_pmu.c +++ b/drivers/perf/qcom_l3_pmu.c @@ -767,11 +767,8 @@ static int qcom_l3_cache_pmu_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, ret, qcom_l3_cache__handle_irq, 0, name, l3pmu); - if (ret) { - dev_err(&pdev->dev, "Request for IRQ failed for slice @%pa\n", - &memrc->start); + if (ret) return ret; - } /* Add this instance to the list used by the offline callback */ ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_QCOM_L3_ONLINE, &l3pmu->node); diff --git a/drivers/perf/starfive_starlink_pmu.c b/drivers/perf/starfive_starlink_pmu.c index b1c7dc4869bd..57b73575c43f 100644 --- a/drivers/perf/starfive_starlink_pmu.c +++ b/drivers/perf/starfive_starlink_pmu.c @@ -435,7 +435,7 @@ static int starlink_setup_irqs(struct starlink_pmu *starlink_pmu, ret = devm_request_irq(&pdev->dev, irq, starlink_pmu_handle_irq, 0, STARLINK_PMU_PDEV_NAME, starlink_pmu); if (ret) - return dev_err_probe(&pdev->dev, ret, "Failed to request IRQ\n"); + return ret; starlink_pmu->irq = irq; diff --git a/drivers/perf/xgene_pmu.c b/drivers/perf/xgene_pmu.c index 33b5497bdc06..b79409c750e6 100644 --- a/drivers/perf/xgene_pmu.c +++ b/drivers/perf/xgene_pmu.c @@ -1876,10 +1876,8 @@ static int xgene_pmu_probe(struct platform_device *pdev) rc = devm_request_irq(&pdev->dev, irq, xgene_pmu_isr, IRQF_NOBALANCING | IRQF_NO_THREAD, dev_name(&pdev->dev), xgene_pmu); - if (rc) { - dev_err(&pdev->dev, "Could not request IRQ %d\n", irq); + if (rc) return rc; - } xgene_pmu->irq = irq; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 1c9d40e09107..2c17239aacea 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2640,6 +2640,8 @@ #define PCI_VENDOR_ID_SUNIX 0x1fd4 #define PCI_DEVICE_ID_SUNIX_1999 0x1999 +#define PCI_VENDOR_ID_PICOHEART 0x20fa + #define PCI_VENDOR_ID_HINT 0x3388 #define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013 diff --git a/include/linux/pcie-dwc.h b/include/linux/pcie-dwc.h index 8ff778e7aec0..d3cd701a58e3 100644 --- a/include/linux/pcie-dwc.h +++ b/include/linux/pcie-dwc.h @@ -26,6 +26,8 @@ static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = { .vsec_id = 0x02, .vsec_rev = 0x4 }, { .vendor_id = PCI_VENDOR_ID_AMPERE, .vsec_id = 0x02, .vsec_rev = 0x4 }, + { .vendor_id = PCI_VENDOR_ID_PICOHEART, + .vsec_id = 0x02, .vsec_rev = 0x4 }, { .vendor_id = PCI_VENDOR_ID_QCOM, .vsec_id = 0x02, .vsec_rev = 0x4 }, { .vendor_id = PCI_VENDOR_ID_ROCKCHIP, diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index 52b37f7bdbf9..02d2c7f45b52 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -119,7 +119,7 @@ struct arm_pmu { /* PMUv3 only */ int pmuver; - bool has_smt; + bool avoid_pmccntr; u64 reg_pmmir; u64 reg_brbidr; #define ARMV8_PMUV3_MAX_COMMON_EVENTS 0x40