Commit d662ed2e authored by Mark Rutland's avatar Mark Rutland Committed by Pawel Moll

bus: arm-ccn: make event groups reliable

The CCN PMU driver leaves the counting logic always enabled, and thus
events are enabled while groups are manipulated. As each event is
stopped and read individually, this leads to arbitrary skew across event
groups, which can be seen if counting several identical events.

To avoid this, implement pmu_{enable,disable} callbacks to stop and
start all counters atomically around event manipulation. As the counters
are now stopped, we cannot poll the cycle counter to wait for events to
drain from the bus. However, as the counters are stopped and the events
will not be read regardless, we can simply allow the bus to drain
naturally.
Signed-off-by: default avatarMark Rutland <mark.rutland@arm.com>
Signed-off-by: default avatarPawel Moll <pawel.moll@arm.com>
parent 5b1e01f3
...@@ -946,20 +946,11 @@ static void arm_ccn_pmu_event_start(struct perf_event *event, int flags) ...@@ -946,20 +946,11 @@ static void arm_ccn_pmu_event_start(struct perf_event *event, int flags)
static void arm_ccn_pmu_event_stop(struct perf_event *event, int flags) static void arm_ccn_pmu_event_stop(struct perf_event *event, int flags)
{ {
struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
struct hw_perf_event *hw = &event->hw; struct hw_perf_event *hw = &event->hw;
u64 timeout;
/* Disable counting, setting the DT bus to pass-through mode */ /* Disable counting, setting the DT bus to pass-through mode */
arm_ccn_pmu_xp_dt_config(event, 0); arm_ccn_pmu_xp_dt_config(event, 0);
/* Let the DT bus drain */
timeout = arm_ccn_pmu_read_counter(ccn, CCN_IDX_PMU_CYCLE_COUNTER) +
ccn->num_xps;
while (arm_ccn_pmu_read_counter(ccn, CCN_IDX_PMU_CYCLE_COUNTER) <
timeout)
cpu_relax();
if (flags & PERF_EF_UPDATE) if (flags & PERF_EF_UPDATE)
arm_ccn_pmu_event_update(event); arm_ccn_pmu_event_update(event);
...@@ -1162,6 +1153,24 @@ static void arm_ccn_pmu_event_read(struct perf_event *event) ...@@ -1162,6 +1153,24 @@ static void arm_ccn_pmu_event_read(struct perf_event *event)
arm_ccn_pmu_event_update(event); arm_ccn_pmu_event_update(event);
} }
static void arm_ccn_pmu_enable(struct pmu *pmu)
{
struct arm_ccn *ccn = pmu_to_arm_ccn(pmu);
u32 val = readl(ccn->dt.base + CCN_DT_PMCR);
val |= CCN_DT_PMCR__PMU_EN;
writel(val, ccn->dt.base + CCN_DT_PMCR);
}
static void arm_ccn_pmu_disable(struct pmu *pmu)
{
struct arm_ccn *ccn = pmu_to_arm_ccn(pmu);
u32 val = readl(ccn->dt.base + CCN_DT_PMCR);
val &= ~CCN_DT_PMCR__PMU_EN;
writel(val, ccn->dt.base + CCN_DT_PMCR);
}
static irqreturn_t arm_ccn_pmu_overflow_handler(struct arm_ccn_dt *dt) static irqreturn_t arm_ccn_pmu_overflow_handler(struct arm_ccn_dt *dt)
{ {
u32 pmovsr = readl(dt->base + CCN_DT_PMOVSR); u32 pmovsr = readl(dt->base + CCN_DT_PMOVSR);
...@@ -1284,6 +1293,8 @@ static int arm_ccn_pmu_init(struct arm_ccn *ccn) ...@@ -1284,6 +1293,8 @@ static int arm_ccn_pmu_init(struct arm_ccn *ccn)
.start = arm_ccn_pmu_event_start, .start = arm_ccn_pmu_event_start,
.stop = arm_ccn_pmu_event_stop, .stop = arm_ccn_pmu_event_stop,
.read = arm_ccn_pmu_event_read, .read = arm_ccn_pmu_event_read,
.pmu_enable = arm_ccn_pmu_enable,
.pmu_disable = arm_ccn_pmu_disable,
}; };
/* No overflow interrupt? Have to use a timer instead. */ /* No overflow interrupt? Have to use a timer instead. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment