[PATCH 1/2] perf/arm-cmn: Revamp model detection
Ilkka Koskinen
ilkka at os.amperecomputing.com
Wed Jun 14 01:24:15 PDT 2023
On Mon, 12 Jun 2023, Robin Murphy wrote:
> CMN implements a set of CoreSight-format peripheral ID registers which
> in principle we should be able to use to identify the hardware. However
> so far we have avoided trying to use the part number field since the
> TRMs have all described it as "configuration dependent". It turns out,
> though, that this is a quirk of the documentation generation process,
> and in fact the part number should always be a stable well-defined field
> which we can trust.
>
> To that end, revamp our model detection to rely less on ACPI/DT, and
> pave the way towards further using the hardware information as an
> identifier for userspace jevent metrics. This includes renaming the
> revision constants to maximise readability.
>
> Signed-off-by: Robin Murphy <robin.murphy at arm.com>
Hi Robin,
I quickly tested on two different SoCs and the patchset seemed to
work fine.
Reviewed-and-tested-by: Ilkka Koskinen <ilkka at os.amperecomputing.com>
> ---
> drivers/perf/arm-cmn.c | 145 ++++++++++++++++++++++++++---------------
> 1 file changed, 93 insertions(+), 52 deletions(-)
>
> diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c
> index 51c703759d3d..8cf4ed932950 100644
> --- a/drivers/perf/arm-cmn.c
> +++ b/drivers/perf/arm-cmn.c
> @@ -44,8 +44,11 @@
> #define CMN_MAX_DTMS (CMN_MAX_XPS + (CMN_MAX_DIMENSION - 1) * 4)
>
> /* The CFG node has various info besides the discovery tree */
> -#define CMN_CFGM_PERIPH_ID_2 0x0010
> -#define CMN_CFGM_PID2_REVISION GENMASK(7, 4)
> +#define CMN_CFGM_PERIPH_ID_01 0x0008
> +#define CMN_CFGM_PID0_PART_0 GENMASK_ULL(7, 0)
> +#define CMN_CFGM_PID1_PART_1 GENMASK_ULL(35, 32)
> +#define CMN_CFGM_PERIPH_ID_23 0x0010
> +#define CMN_CFGM_PID2_REVISION GENMASK_ULL(7, 4)
>
> #define CMN_CFGM_INFO_GLOBAL 0x900
> #define CMN_INFO_MULTIPLE_DTM_EN BIT_ULL(63)
> @@ -186,6 +189,7 @@
> #define CMN_WP_DOWN 2
>
>
> +/* Internal values for encoding event support */
> enum cmn_model {
> CMN600 = 1,
> CMN650 = 2,
> @@ -197,26 +201,34 @@ enum cmn_model {
> CMN_650ON = CMN650 | CMN700,
> };
>
> +/* Actual part numbers and revision IDs defined by the hardware */
> +enum cmn_part {
> + PART_CMN600 = 0x434,
> + PART_CMN650 = 0x436,
> + PART_CMN700 = 0x43c,
> + PART_CI700 = 0x43a,
> +};
> +
> /* CMN-600 r0px shouldn't exist in silicon, thankfully */
> enum cmn_revision {
> - CMN600_R1P0,
> - CMN600_R1P1,
> - CMN600_R1P2,
> - CMN600_R1P3,
> - CMN600_R2P0,
> - CMN600_R3P0,
> - CMN600_R3P1,
> - CMN650_R0P0 = 0,
> - CMN650_R1P0,
> - CMN650_R1P1,
> - CMN650_R2P0,
> - CMN650_R1P2,
> - CMN700_R0P0 = 0,
> - CMN700_R1P0,
> - CMN700_R2P0,
> - CI700_R0P0 = 0,
> - CI700_R1P0,
> - CI700_R2P0,
> + REV_CMN600_R1P0,
> + REV_CMN600_R1P1,
> + REV_CMN600_R1P2,
> + REV_CMN600_R1P3,
> + REV_CMN600_R2P0,
> + REV_CMN600_R3P0,
> + REV_CMN600_R3P1,
> + REV_CMN650_R0P0 = 0,
> + REV_CMN650_R1P0,
> + REV_CMN650_R1P1,
> + REV_CMN650_R2P0,
> + REV_CMN650_R1P2,
> + REV_CMN700_R0P0 = 0,
> + REV_CMN700_R1P0,
> + REV_CMN700_R2P0,
> + REV_CI700_R0P0 = 0,
> + REV_CI700_R1P0,
> + REV_CI700_R2P0,
> };
>
> enum cmn_node_type {
> @@ -306,7 +318,7 @@ struct arm_cmn {
> unsigned int state;
>
> enum cmn_revision rev;
> - enum cmn_model model;
> + enum cmn_part part;
> u8 mesh_x;
> u8 mesh_y;
> u16 num_xps;
> @@ -394,19 +406,35 @@ static struct arm_cmn_node *arm_cmn_node(const struct arm_cmn *cmn,
> return NULL;
> }
>
> +static enum cmn_model arm_cmn_model(const struct arm_cmn *cmn)
> +{
> + switch (cmn->part) {
> + case PART_CMN600:
> + return CMN600;
> + case PART_CMN650:
> + return CMN650;
> + case PART_CMN700:
> + return CMN700;
> + case PART_CI700:
> + return CI700;
> + default:
> + return 0;
> + };
> +}
> +
> static u32 arm_cmn_device_connect_info(const struct arm_cmn *cmn,
> const struct arm_cmn_node *xp, int port)
> {
> int offset = CMN_MXP__CONNECT_INFO(port);
>
> if (port >= 2) {
> - if (cmn->model & (CMN600 | CMN650))
> + if (cmn->part == PART_CMN600 || cmn->part == PART_CMN650)
> return 0;
> /*
> * CI-700 may have extra ports, but still has the
> * mesh_port_connect_info registers in the way.
> */
> - if (cmn->model == CI700)
> + if (cmn->part == PART_CI700)
> offset += CI700_CONNECT_INFO_P2_5_OFFSET;
> }
>
> @@ -640,7 +668,7 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
>
> eattr = container_of(attr, typeof(*eattr), attr.attr);
>
> - if (!(eattr->model & cmn->model))
> + if (!(eattr->model & arm_cmn_model(cmn)))
> return 0;
>
> type = eattr->type;
> @@ -658,7 +686,7 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
> if ((intf & 4) && !(cmn->ports_used & BIT(intf & 3)))
> return 0;
>
> - if (chan == 4 && cmn->model == CMN600)
> + if (chan == 4 && cmn->part == PART_CMN600)
> return 0;
>
> if ((chan == 5 && cmn->rsp_vc_num < 2) ||
> @@ -669,19 +697,19 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
> }
>
> /* Revision-specific differences */
> - if (cmn->model == CMN600) {
> - if (cmn->rev < CMN600_R1P3) {
> + if (cmn->part == PART_CMN600) {
> + if (cmn->rev < REV_CMN600_R1P3) {
> if (type == CMN_TYPE_CXRA && eventid > 0x10)
> return 0;
> }
> - if (cmn->rev < CMN600_R1P2) {
> + if (cmn->rev < REV_CMN600_R1P2) {
> if (type == CMN_TYPE_HNF && eventid == 0x1b)
> return 0;
> if (type == CMN_TYPE_CXRA || type == CMN_TYPE_CXHA)
> return 0;
> }
> - } else if (cmn->model == CMN650) {
> - if (cmn->rev < CMN650_R2P0 || cmn->rev == CMN650_R1P2) {
> + } else if (cmn->part == PART_CMN650) {
> + if (cmn->rev < REV_CMN650_R2P0 || cmn->rev == REV_CMN650_R1P2) {
> if (type == CMN_TYPE_HNF && eventid > 0x22)
> return 0;
> if (type == CMN_TYPE_SBSX && eventid == 0x17)
> @@ -689,8 +717,8 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
> if (type == CMN_TYPE_RNI && eventid > 0x10)
> return 0;
> }
> - } else if (cmn->model == CMN700) {
> - if (cmn->rev < CMN700_R2P0) {
> + } else if (cmn->part == PART_CMN700) {
> + if (cmn->rev < REV_CMN700_R2P0) {
> if (type == CMN_TYPE_HNF && eventid > 0x2c)
> return 0;
> if (type == CMN_TYPE_CCHA && eventid > 0x74)
> @@ -698,7 +726,7 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
> if (type == CMN_TYPE_CCLA && eventid > 0x27)
> return 0;
> }
> - if (cmn->rev < CMN700_R1P0) {
> + if (cmn->rev < REV_CMN700_R1P0) {
> if (type == CMN_TYPE_HNF && eventid > 0x2b)
> return 0;
> }
> @@ -1200,7 +1228,7 @@ static u32 arm_cmn_wp_config(struct perf_event *event)
> u32 grp = CMN_EVENT_WP_GRP(event);
> u32 exc = CMN_EVENT_WP_EXCLUSIVE(event);
> u32 combine = CMN_EVENT_WP_COMBINE(event);
> - bool is_cmn600 = to_cmn(event->pmu)->model == CMN600;
> + bool is_cmn600 = to_cmn(event->pmu)->part == PART_CMN600;
>
> config = FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_DEV_SEL, dev) |
> FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_CHN_SEL, chn) |
> @@ -1520,14 +1548,14 @@ 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(enum cmn_model model,
> +static enum cmn_filter_select arm_cmn_filter_sel(const struct arm_cmn *cmn,
> enum cmn_node_type type,
> unsigned int eventid)
> {
> struct arm_cmn_event_attr *e;
> - int i;
> + enum cmn_model model = arm_cmn_model(cmn);
>
> - for (i = 0; i < ARRAY_SIZE(arm_cmn_event_attrs) - 1; i++) {
> + 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;
> @@ -1570,12 +1598,12 @@ 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;
> - } else if (type == CMN_TYPE_XP && cmn->model == CMN700) {
> + } else if (type == CMN_TYPE_XP && cmn->part == PART_CMN700) {
> hw->wide_sel = true;
> }
>
> /* This is sufficiently annoying to recalculate, so cache it */
> - hw->filter_sel = arm_cmn_filter_sel(cmn->model, type, eventid);
> + hw->filter_sel = arm_cmn_filter_sel(cmn, type, eventid);
>
> bynodeid = CMN_EVENT_BYNODEID(event);
> nodeid = CMN_EVENT_NODEID(event);
> @@ -2055,6 +2083,7 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
> void __iomem *cfg_region;
> struct arm_cmn_node cfg, *dn;
> struct arm_cmn_dtm *dtm;
> + enum cmn_part part;
> u16 child_count, child_poff;
> u32 xp_offset[CMN_MAX_XPS];
> u64 reg;
> @@ -2066,7 +2095,19 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
> return -ENODEV;
>
> cfg_region = cmn->base + rgn_offset;
> - reg = readl_relaxed(cfg_region + CMN_CFGM_PERIPH_ID_2);
> +
> + 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;
> + if (cmn->part && cmn->part != part)
> + dev_warn(cmn->dev,
> + "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);
>
> reg = readq_relaxed(cfg_region + CMN_CFGM_INFO_GLOBAL);
> @@ -2130,7 +2171,7 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
> if (xp->id == (1 << 3))
> cmn->mesh_x = xp->logid;
>
> - if (cmn->model == CMN600)
> + if (cmn->part == PART_CMN600)
> xp->dtc = 0xf;
> else
> xp->dtc = 1 << readl_relaxed(xp_region + CMN_DTM_UNIT_INFO);
> @@ -2251,7 +2292,7 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
> if (cmn->num_xps == 1)
> dev_warn(cmn->dev, "1x1 config not fully supported, translate XP events manually\n");
>
> - dev_dbg(cmn->dev, "model %d, periph_id_2 revision %d\n", cmn->model, cmn->rev);
> + dev_dbg(cmn->dev, "periph_id part 0x%03x revision %d\n", cmn->part, cmn->rev);
> reg = cmn->ports_used;
> dev_dbg(cmn->dev, "mesh %dx%d, ID width %d, ports %6pbl%s\n",
> cmn->mesh_x, cmn->mesh_y, arm_cmn_xyidbits(cmn), ®,
> @@ -2306,17 +2347,17 @@ static int arm_cmn_probe(struct platform_device *pdev)
> return -ENOMEM;
>
> cmn->dev = &pdev->dev;
> - cmn->model = (unsigned long)device_get_match_data(cmn->dev);
> + cmn->part = (unsigned long)device_get_match_data(cmn->dev);
> platform_set_drvdata(pdev, cmn);
>
> - if (cmn->model == CMN600 && has_acpi_companion(cmn->dev)) {
> + if (cmn->part == PART_CMN600 && has_acpi_companion(cmn->dev)) {
> rootnode = arm_cmn600_acpi_probe(pdev, cmn);
> } else {
> rootnode = 0;
> cmn->base = devm_platform_ioremap_resource(pdev, 0);
> if (IS_ERR(cmn->base))
> return PTR_ERR(cmn->base);
> - if (cmn->model == CMN600)
> + if (cmn->part == PART_CMN600)
> rootnode = arm_cmn600_of_probe(pdev->dev.of_node);
> }
> if (rootnode < 0)
> @@ -2404,10 +2445,10 @@ static int arm_cmn_remove(struct platform_device *pdev)
>
> #ifdef CONFIG_OF
> static const struct of_device_id arm_cmn_of_match[] = {
> - { .compatible = "arm,cmn-600", .data = (void *)CMN600 },
> - { .compatible = "arm,cmn-650", .data = (void *)CMN650 },
> - { .compatible = "arm,cmn-700", .data = (void *)CMN700 },
> - { .compatible = "arm,ci-700", .data = (void *)CI700 },
> + { .compatible = "arm,cmn-600", .data = (void *)PART_CMN600 },
> + { .compatible = "arm,cmn-650" },
> + { .compatible = "arm,cmn-700" },
> + { .compatible = "arm,ci-700" },
> {}
> };
> MODULE_DEVICE_TABLE(of, arm_cmn_of_match);
> @@ -2415,9 +2456,9 @@ MODULE_DEVICE_TABLE(of, arm_cmn_of_match);
>
> #ifdef CONFIG_ACPI
> static const struct acpi_device_id arm_cmn_acpi_match[] = {
> - { "ARMHC600", CMN600 },
> - { "ARMHC650", CMN650 },
> - { "ARMHC700", CMN700 },
> + { "ARMHC600", PART_CMN600 },
> + { "ARMHC650" },
> + { "ARMHC700" },
> {}
> };
> MODULE_DEVICE_TABLE(acpi, arm_cmn_acpi_match);
> --
> 2.39.2.101.g768bb238c484.dirty
>
>
More information about the linux-arm-kernel
mailing list