Skip to content

Commit

Permalink
perf/arm-cmn: Revamp model detection
Browse files Browse the repository at this point in the history
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@arm.com>
Reviewed-and-tested-by: Ilkka Koskinen <ilkka@os.amperecomputing.com>
Link: https://lore.kernel.org/r/3c791eaae814b0126f9adbd5419bfb4a600dade7.1686588640.git.robin.murphy@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
  • Loading branch information
Robin Murphy authored and Will Deacon committed Jun 16, 2023
1 parent 95f5819 commit 7819e05
Showing 1 changed file with 93 additions and 52 deletions.
145 changes: 93 additions & 52 deletions drivers/perf/arm-cmn.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -186,6 +189,7 @@
#define CMN_WP_DOWN 2


/* Internal values for encoding event support */
enum cmn_model {
CMN600 = 1,
CMN650 = 2,
Expand All @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand All @@ -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) ||
Expand All @@ -669,36 +697,36 @@ 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)
return 0;
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)
return 0;
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;
}
Expand Down Expand Up @@ -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) |
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -2007,6 +2035,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;
Expand All @@ -2018,7 +2047,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);
Expand Down Expand Up @@ -2082,7 +2123,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);
Expand Down Expand Up @@ -2202,7 +2243,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), &reg,
Expand Down Expand Up @@ -2257,17 +2298,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)
Expand Down Expand Up @@ -2336,20 +2377,20 @@ 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);
#endif

#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);
Expand Down

0 comments on commit 7819e05

Please sign in to comment.