summaryrefslogtreecommitdiff
path: root/drivers/perf
diff options
context:
space:
mode:
authorTiberiu Breana <andrei-tiberiu.breana@nxp.com>2017-06-14 17:50:11 +0300
committerJason Liu <jason.hui.liu@nxp.com>2019-02-12 10:27:07 +0800
commit1309cf632f99b0227df7a177f9b64f93aad0b5df (patch)
tree19c8d4d9950ca0d1b85130de50dd11b7f532c320 /drivers/perf
parentec5ce5e04022a27923e00457d4ca93b08c5499b3 (diff)
MLK-13855-3: perf: ddr-perf: Add counter overflow handling
Added support for counter overflow interrupts. When the cycles counter overflows, update all local event data, then reset it and let it continue counting. Signed-off-by: Tiberiu Breana <andrei-tiberiu.breana@nxp.com>
Diffstat (limited to 'drivers/perf')
-rw-r--r--drivers/perf/ddr-perf.c64
1 files changed, 64 insertions, 0 deletions
diff --git a/drivers/perf/ddr-perf.c b/drivers/perf/ddr-perf.c
index 7bc05859d4bb..2ec4ce9aa73d 100644
--- a/drivers/perf/ddr-perf.c
+++ b/drivers/perf/ddr-perf.c
@@ -13,6 +13,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/of_irq.h>
#include <linux/perf_event.h>
#include <linux/slab.h>
@@ -25,6 +26,7 @@
#define CNTL_EN 0x4
#define CNTL_EN_MASK 0xFFFFFFFB
#define CNTL_CLEAR_MASK 0xFFFFFFFD
+#define CNTL_OVER_MASK 0xFFFFFFFE
#define CNTL_CSV_SHIFT 24
#define CNTL_CSV_MASK (0xFF << CNTL_CSV_SHIFT)
@@ -360,14 +362,57 @@ static int ddr_perf_init(struct ddr_pmu *pmu, void __iomem *base,
return ida_simple_get(&ddr_ida, 0, 0, GFP_KERNEL);
}
+static irqreturn_t ddr_perf_irq_handler(int irq, void *p)
+{
+ int i;
+ u8 reg;
+ int val;
+ int counter;
+ struct ddr_pmu *pmu = (struct ddr_pmu *) p;
+ struct perf_event *event;
+
+ /*
+ * The cycles counter has overflowed. Update all of the local counter
+ * values, then reset the cycles counter, so the others can continue
+ * counting.
+ */
+ for (i = 0; i <= pmu->total_events; i++) {
+ if (pmu->active_events[i] != NULL) {
+ event = pmu->active_events[i];
+ counter = event->hw.idx;
+ reg = counter * 4 + COUNTER_CNTL;
+ val = readl(pmu->base + reg);
+ ddr_perf_event_update(event);
+ if (val & CNTL_OVER) {
+ /* Clear counter, then re-enable it. */
+ ddr_perf_event_enable(pmu, event->attr.config,
+ counter, true);
+ /* Update event again to reset prev_count */
+ ddr_perf_event_update(event);
+ }
+ }
+ }
+
+ /*
+ * Reset the cycles counter regardless if it was explicitly
+ * enabled or not.
+ */
+ ddr_perf_event_enable(pmu, EVENT_CYCLES_ID,
+ EVENT_CYCLES_COUNTER, true);
+
+ return IRQ_HANDLED;
+}
+
static int ddr_perf_probe(struct platform_device *pdev)
{
struct ddr_pmu *pmu;
+ struct device_node *np;
void __iomem *base;
struct resource *iomem;
char *name;
int num;
int ret;
+ u32 irq;
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, iomem);
@@ -376,6 +421,8 @@ static int ddr_perf_probe(struct platform_device *pdev)
return ret;
}
+ np = pdev->dev.of_node;
+
pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
if (!pmu)
return -ENOMEM;
@@ -388,6 +435,23 @@ static int ddr_perf_probe(struct platform_device *pdev)
if (ret)
goto ddr_perf_err;
+ /* Request irq */
+ irq = of_irq_get(np, 0);
+ if (irq < 0) {
+ pr_err("Failed to get irq: %d", irq);
+ goto ddr_perf_err;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq,
+ ddr_perf_irq_handler, NULL,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ DDR_PERF_DEV_NAME,
+ pmu);
+ if (ret < 0) {
+ pr_err("Request irq failed: %d", ret);
+ goto ddr_perf_err;
+ }
+
return 0;
ddr_perf_err: