summaryrefslogtreecommitdiff
path: root/drivers/perf/arm-ni.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/perf/arm-ni.c')
-rw-r--r--drivers/perf/arm-ni.c97
1 files changed, 75 insertions, 22 deletions
diff --git a/drivers/perf/arm-ni.c b/drivers/perf/arm-ni.c
index 1615a0564031..66858c65215d 100644
--- a/drivers/perf/arm-ni.c
+++ b/drivers/perf/arm-ni.c
@@ -21,6 +21,11 @@
#define NI_CHILD_NODE_INFO 0x004
#define NI_CHILD_PTR(n) (0x008 + (n) * 4)
+#define NI_NUM_SUB_FEATURES 0x100
+#define NI_SUB_FEATURE_TYPE(n) (0x108 + (n) * 8)
+#define NI_SUB_FEATURE_PTR(n) (0x10c + (n) * 8)
+
+#define NI_SUB_FEATURE_TYPE_FCU 0x2
#define NI700_PMUSELA 0x00c
@@ -33,9 +38,10 @@
#define NI_PIDR2_VERSION GENMASK(7, 4)
/* PMU node */
-#define NI_PMEVCNTR(n) (0x008 + (n) * 8)
-#define NI_PMCCNTR_L 0x0f8
-#define NI_PMCCNTR_U 0x0fc
+#define NI700_PMEVCNTR(n) (0x008 + (n) * 8)
+#define NI700_PMCCNTR_L 0x0f8
+#define NI_PMEVCNTR(n) (0x200 + (n) * 8)
+#define NI_PMCCNTR_L 0x2f8
#define NI_PMEVTYPER(n) (0x400 + (n) * 4)
#define NI_PMEVTYPER_NODE_TYPE GENMASK(12, 9)
#define NI_PMEVTYPER_NODE_ID GENMASK(8, 0)
@@ -66,6 +72,8 @@
enum ni_part {
PART_NI_700 = 0x43b,
PART_NI_710AE = 0x43d,
+ PART_NOC_S3 = 0x43f,
+ PART_SI_L1 = 0x455,
};
enum ni_node_type {
@@ -79,6 +87,10 @@ enum ni_node_type {
NI_HSNI,
NI_HMNI,
NI_PMNI,
+ NI_TSNI,
+ NI_TMNI,
+ NI_CMNI = 0x0e,
+ NI_MCN = 0x63,
};
struct arm_ni_node {
@@ -179,6 +191,9 @@ static struct attribute *arm_ni_event_attrs[] = {
NI_EVENT_ATTR(hsni, NI_HSNI),
NI_EVENT_ATTR(hmni, NI_HMNI),
NI_EVENT_ATTR(pmni, NI_PMNI),
+ NI_EVENT_ATTR(tsni, NI_TSNI),
+ NI_EVENT_ATTR(tmni, NI_TMNI),
+ NI_EVENT_ATTR(cmni, NI_CMNI),
NULL
};
@@ -308,9 +323,15 @@ static int arm_ni_validate_group(struct perf_event *event)
return 0;
}
+static bool arm_ni_is_7xx(const struct arm_ni *ni)
+{
+ return ni->part == PART_NI_700 || ni->part == PART_NI_710AE;
+}
+
static int arm_ni_event_init(struct perf_event *event)
{
struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
+ struct arm_ni *ni;
if (event->attr.type != event->pmu->type)
return -ENOENT;
@@ -318,7 +339,10 @@ static int arm_ni_event_init(struct perf_event *event)
if (is_sampling_event(event))
return -EINVAL;
- event->cpu = cd_to_ni(cd)->cpu;
+ ni = cd_to_ni(cd);
+ event->cpu = ni->cpu;
+ event->hw.flags = arm_ni_is_7xx(ni);
+
if (NI_EVENT_TYPE(event) == NI_PMU)
return arm_ni_validate_group(event);
@@ -332,16 +356,16 @@ static int arm_ni_event_init(struct perf_event *event)
return -EINVAL;
}
-static u64 arm_ni_read_ccnt(struct arm_ni_cd *cd)
+static u64 arm_ni_read_ccnt(void __iomem *pmccntr)
{
u64 l, u_old, u_new;
int retries = 3; /* 1st time unlucky, 2nd improbable, 3rd just broken */
- u_new = readl_relaxed(cd->pmu_base + NI_PMCCNTR_U);
+ u_new = readl_relaxed(pmccntr + 4);
do {
u_old = u_new;
- l = readl_relaxed(cd->pmu_base + NI_PMCCNTR_L);
- u_new = readl_relaxed(cd->pmu_base + NI_PMCCNTR_U);
+ l = readl_relaxed(pmccntr);
+ u_new = readl_relaxed(pmccntr + 4);
} while (u_new != u_old && --retries);
WARN_ON(!retries);
@@ -350,7 +374,6 @@ static u64 arm_ni_read_ccnt(struct arm_ni_cd *cd)
static void arm_ni_event_read(struct perf_event *event)
{
- struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
struct hw_perf_event *hw = &event->hw;
u64 count, prev;
bool ccnt = hw->idx == NI_CCNT_IDX;
@@ -358,9 +381,9 @@ static void arm_ni_event_read(struct perf_event *event)
do {
prev = local64_read(&hw->prev_count);
if (ccnt)
- count = arm_ni_read_ccnt(cd);
+ count = arm_ni_read_ccnt((void __iomem *)event->hw.event_base);
else
- count = readl_relaxed(cd->pmu_base + NI_PMEVCNTR(hw->idx));
+ count = readl_relaxed((void __iomem *)event->hw.event_base);
} while (local64_cmpxchg(&hw->prev_count, prev, count) != prev);
count -= prev;
@@ -385,16 +408,16 @@ static void arm_ni_event_stop(struct perf_event *event, int flags)
arm_ni_event_read(event);
}
-static void arm_ni_init_ccnt(struct arm_ni_cd *cd)
+static void arm_ni_init_ccnt(struct hw_perf_event *hw)
{
- local64_set(&cd->ccnt->hw.prev_count, S64_MIN);
- lo_hi_writeq_relaxed(S64_MIN, cd->pmu_base + NI_PMCCNTR_L);
+ local64_set(&hw->prev_count, S64_MIN);
+ lo_hi_writeq_relaxed(S64_MIN, (void __iomem *)hw->event_base);
}
-static void arm_ni_init_evcnt(struct arm_ni_cd *cd, int idx)
+static void arm_ni_init_evcnt(struct hw_perf_event *hw)
{
- local64_set(&cd->evcnt[idx]->hw.prev_count, S32_MIN);
- writel_relaxed(S32_MIN, cd->pmu_base + NI_PMEVCNTR(idx));
+ local64_set(&hw->prev_count, S32_MIN);
+ writel_relaxed(S32_MIN, (void __iomem *)hw->event_base);
}
static int arm_ni_event_add(struct perf_event *event, int flags)
@@ -409,8 +432,10 @@ static int arm_ni_event_add(struct perf_event *event, int flags)
if (cd->ccnt)
return -ENOSPC;
hw->idx = NI_CCNT_IDX;
+ hw->event_base = (unsigned long)cd->pmu_base +
+ (hw->flags ? NI700_PMCCNTR_L : NI_PMCCNTR_L);
cd->ccnt = event;
- arm_ni_init_ccnt(cd);
+ arm_ni_init_ccnt(hw);
} else {
hw->idx = 0;
while (cd->evcnt[hw->idx]) {
@@ -420,7 +445,9 @@ static int arm_ni_event_add(struct perf_event *event, int flags)
cd->evcnt[hw->idx] = event;
unit = (void *)hw->config_base;
unit->event[hw->idx] = NI_EVENT_EVENTID(event);
- arm_ni_init_evcnt(cd, hw->idx);
+ hw->event_base = (unsigned long)cd->pmu_base +
+ (hw->flags ? NI700_PMEVCNTR(hw->idx) : NI_PMEVCNTR(hw->idx));
+ arm_ni_init_evcnt(hw);
lo_hi_writeq_relaxed(le64_to_cpu(unit->pmusel), unit->pmusela);
reg = FIELD_PREP(NI_PMEVTYPER_NODE_TYPE, type) |
@@ -457,7 +484,7 @@ static irqreturn_t arm_ni_handle_irq(int irq, void *dev_id)
ret = IRQ_HANDLED;
if (!(WARN_ON(!cd->ccnt))) {
arm_ni_event_read(cd->ccnt);
- arm_ni_init_ccnt(cd);
+ arm_ni_init_ccnt(&cd->ccnt->hw);
}
}
for (int i = 0; i < NI_NUM_COUNTERS; i++) {
@@ -466,7 +493,7 @@ static irqreturn_t arm_ni_handle_irq(int irq, void *dev_id)
ret = IRQ_HANDLED;
if (!(WARN_ON(!cd->evcnt[i]))) {
arm_ni_event_read(cd->evcnt[i]);
- arm_ni_init_evcnt(cd, i);
+ arm_ni_init_evcnt(&cd->evcnt[i]->hw);
}
}
writel_relaxed(reg, cd->pmu_base + NI_PMOVSCLR);
@@ -476,6 +503,25 @@ static irqreturn_t arm_ni_handle_irq(int irq, void *dev_id)
}
}
+static void __iomem *arm_ni_get_pmusel(struct arm_ni *ni, void __iomem *unit_base)
+{
+ u32 type, ptr, num;
+
+ if (arm_ni_is_7xx(ni))
+ return unit_base + NI700_PMUSELA;
+
+ num = readl_relaxed(unit_base + NI_NUM_SUB_FEATURES);
+ for (int i = 0; i < num; i++) {
+ type = readl_relaxed(unit_base + NI_SUB_FEATURE_TYPE(i));
+ if (type != NI_SUB_FEATURE_TYPE_FCU)
+ continue;
+ ptr = readl_relaxed(unit_base + NI_SUB_FEATURE_PTR(i));
+ return ni->base + ptr;
+ }
+ /* Should be impossible */
+ return NULL;
+}
+
static int arm_ni_init_cd(struct arm_ni *ni, struct arm_ni_node *node, u64 res_start)
{
struct arm_ni_cd *cd = ni->cds + node->id;
@@ -512,13 +558,18 @@ static int arm_ni_init_cd(struct arm_ni *ni, struct arm_ni_node *node, u64 res_s
case NI_HSNI:
case NI_HMNI:
case NI_PMNI:
- unit->pmusela = unit_base + NI700_PMUSELA;
+ case NI_TSNI:
+ case NI_TMNI:
+ case NI_CMNI:
+ unit->pmusela = arm_ni_get_pmusel(ni, unit_base);
writel_relaxed(1, unit->pmusela);
if (readl_relaxed(unit->pmusela) != 1)
dev_info(ni->dev, "No access to node 0x%04x%04x\n", unit->id, unit->type);
else
unit->ns = true;
break;
+ case NI_MCN:
+ break;
default:
/*
* e.g. FMU - thankfully bits 3:2 of FMU_ERR_FR0 are RES0 so
@@ -649,6 +700,8 @@ static int arm_ni_probe(struct platform_device *pdev)
switch (part) {
case PART_NI_700:
case PART_NI_710AE:
+ case PART_NOC_S3:
+ case PART_SI_L1:
break;
default:
dev_WARN(&pdev->dev, "Unknown part number: 0x%03x, this may go badly\n", part);