in arm-cmn.c [1287:1392]
static int arm_cmn_event_add(struct perf_event *event, int flags)
{
struct arm_cmn *cmn = to_cmn(event->pmu);
struct arm_cmn_hw_event *hw = to_cmn_hw(event);
struct arm_cmn_dtc *dtc = &cmn->dtc[0];
struct arm_cmn_node *dn;
enum cmn_node_type type = CMN_EVENT_TYPE(event);
unsigned int i, dtc_idx, input_sel;
if (type == CMN_TYPE_DTC) {
i = 0;
while (cmn->dtc[i].cycles)
if (++i == cmn->num_dtcs)
return -ENOSPC;
cmn->dtc[i].cycles = event;
hw->dtc_idx = CMN_DT_NUM_COUNTERS;
hw->dtcs_used = 1U << i;
if (flags & PERF_EF_START)
arm_cmn_event_start(event, 0);
return 0;
}
/* Grab a free global counter first... */
dtc_idx = 0;
while (dtc->counters[dtc_idx])
if (++dtc_idx == CMN_DT_NUM_COUNTERS)
return -ENOSPC;
hw->dtc_idx = dtc_idx;
/* ...then the local counters to feed it. */
for_each_hw_dn(hw, dn, i) {
struct arm_cmn_dtm *dtm = &cmn->dtms[dn->dtm] + hw->dtm_offset;
unsigned int dtm_idx, shift;
u64 reg;
dtm_idx = 0;
while (dtm->pmu_config_low & CMN__PMEVCNT_PAIRED(dtm_idx))
if (++dtm_idx == CMN_DTM_NUM_COUNTERS)
goto free_dtms;
if (type == CMN_TYPE_XP) {
input_sel = CMN__PMEVCNT0_INPUT_SEL_XP + dtm_idx;
} else if (type == CMN_TYPE_WP) {
int tmp, wp_idx = arm_cmn_wp_idx(event);
u32 cfg = arm_cmn_wp_config(event);
if (dtm->wp_event[wp_idx] >= 0)
goto free_dtms;
tmp = dtm->wp_event[wp_idx ^ 1];
if (tmp >= 0 && CMN_EVENT_WP_COMBINE(event) !=
CMN_EVENT_WP_COMBINE(dtc->counters[tmp]))
goto free_dtms;
input_sel = CMN__PMEVCNT0_INPUT_SEL_WP + wp_idx;
dtm->wp_event[wp_idx] = dtc_idx;
writel_relaxed(cfg, dtm->base + CMN_DTM_WPn_CONFIG(wp_idx));
} else {
struct arm_cmn_nodeid nid = arm_cmn_nid(cmn, dn->id);
if (cmn->multi_dtm)
nid.port %= 2;
input_sel = CMN__PMEVCNT0_INPUT_SEL_DEV + dtm_idx +
(nid.port << 4) + (nid.dev << 2);
if (arm_cmn_is_occup_event(cmn->model, type, CMN_EVENT_EVENTID(event))) {
u8 occupid = CMN_EVENT_OCCUPID(event);
if (dn->occupid_count == 0) {
dn->occupid_val = occupid;
writel_relaxed(occupid,
dn->pmu_base + CMN_PMU_EVENT_SEL + 4);
} else if (dn->occupid_val != occupid) {
goto free_dtms;
}
dn->occupid_count++;
}
}
arm_cmn_set_index(hw->dtm_idx, i, dtm_idx);
dtm->input_sel[dtm_idx] = input_sel;
shift = CMN__PMEVCNTn_GLOBAL_NUM_SHIFT(dtm_idx);
dtm->pmu_config_low &= ~(CMN__PMEVCNT0_GLOBAL_NUM << shift);
dtm->pmu_config_low |= FIELD_PREP(CMN__PMEVCNT0_GLOBAL_NUM, dtc_idx) << shift;
dtm->pmu_config_low |= CMN__PMEVCNT_PAIRED(dtm_idx);
reg = (u64)le32_to_cpu(dtm->pmu_config_high) << 32 | dtm->pmu_config_low;
writeq_relaxed(reg, dtm->base + CMN_DTM_PMU_CONFIG);
}
/* Go go go! */
arm_cmn_init_counter(event);
if (flags & PERF_EF_START)
arm_cmn_event_start(event, 0);
return 0;
free_dtms:
arm_cmn_event_clear(cmn, event, i);
return -ENOSPC;
}