From patchwork Fri Jun 22 19:49:08 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Agustin Vega-Frias X-Patchwork-Id: 10482983 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E196E60383 for ; Fri, 22 Jun 2018 20:03:30 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D237128470 for ; Fri, 22 Jun 2018 20:03:30 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C6CF528488; Fri, 22 Jun 2018 20:03:30 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id A0B5628470 for ; Fri, 22 Jun 2018 20:03:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=O9mogQUHWTTn5GhAor8bQ3M1dK4+UpLJcERucCe2KGU=; b=u9+zUjiOudy30qZSKY5Y1LWRdH s0QHJ94A/iZ3pFdpBjTcLKJSlGH+i++SqWmhiCBdKH/v8Yclvnegf8dd4uZ2NbPwvsZBzqnB9J0qJ PPAMzUyEQ+EvKYukB3F7OD4C/DQTwXgZOaBqMjV9B6jHKHzappEjqlPNgMLyLK89SSIqp2OcSDjNd CDlbvGRPgj26mwXg7/uLNMq2wQ7C/142gv02G7d5o5tmzgpj2yYUlEBOnI5Tlg6hriEt84DFgIcP5 k9mZ8BZGbdfMK9/cMi6JxZmM3HnpkF+skv6sCx/dcvbeTjoLxs2FQf7vuHMHGbJx0DFwxt1QnYt60 jeFpLQvA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1fWSH1-0001TI-HL; Fri, 22 Jun 2018 20:03:11 +0000 Received: from casper.infradead.org ([2001:8b0:10b:1236::1]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1fWSGc-00017k-RR for linux-arm-kernel@bombadil.infradead.org; Fri, 22 Jun 2018 20:02:47 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=casper.20170209; h=References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Sender:Reply-To:MIME-Version:Content-Type: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id: List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=KTG4+8xZE89Edb5tf0smCYqNBIk7tdjRfiWEpRzXR8E=; b=HbaMjbQdw/zs2/pUHxVteW5QO nDswvh1BGvJFDwFly35MseALZB5RaLa9RYmC4+s8L885VTecm+DJzhwdhUXwclMvWFKKx/IzQpelH LRLqkvWZ53dX1ycg33XbY+uvg3SV749GL7a7IE6zQ9Okt1ZdL4Vlp33GDfib3CkYaibbxbVODbc+8 5adWD2tFuYTpgDksaV9MbN6m9o3P/cH+khHMSxnY1mQBMup72RwfDy5R00VFU/kgkzE1JQ58G4Pkk tSIqvGKpRfzqBqblZHeWvaUp3JLlABkUibLEzggHmtCNh40SqUdY6fEYNPfWyhEfMTlAdDKezZQ0W YZGjdKqeg==; Received: from smtp.codeaurora.org ([198.145.29.96]) by casper.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1fWS3o-0001Np-U9 for linux-arm-kernel@lists.infradead.org; Fri, 22 Jun 2018 19:49:36 +0000 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 798AB60B11; Fri, 22 Jun 2018 19:49:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1529696961; bh=rpznV+vRYq6rhEQPQpz7khbZ+M8US7Duvfpu65D+lAM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=a5aJCA7xPJBV2s3mHcBXOBL1rXDq/YX2HgaciJMFQZpnqkcEJL5xRCXdA2yEG5NJi Xi1pSgb+tYOMUMZuQsJSFYE2z/1P+8CAomMbcuiMr+YFHgKMuz/JXDk1V/vBM7Wa3/ pIPjiXSmRFyH7KCAQuVxDSCoVHbVJeTxEgqVV9Y8= Received: from azshara.qualcomm.com (global_nat1_iad_fw.qualcomm.com [129.46.232.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: agustinv@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id A7B9F60B16; Fri, 22 Jun 2018 19:49:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1529696959; bh=rpznV+vRYq6rhEQPQpz7khbZ+M8US7Duvfpu65D+lAM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=oz7Ep32dAVU61FfS9buHja+VAe49uXDA42fvW/5hY35EAdJ5V4G/CAVzZxJsxLWhG Y7/bshVNtA5ssWMwoZSqlw80HmPPcyXOW1sFEJgEfoIwhB7pNn0e3vPNDnBqowUnfe 1iWWC1gxxn9IQy3K0Ff8Eyd0i25keyDXJtXGNXsU= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org A7B9F60B16 Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=agustinv@codeaurora.org From: Agustin Vega-Frias To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, Will Deacon , Mark Rutland , Jeremy Linton , Catalin Marinas , Marc Zyngier , Lorenzo Pieralisi , "Rafael J. Wysocki" Subject: [RFC V3 3/3] perf: qcom: Add Falkor CPU PMU IMPLEMENTATION DEFINED event support Date: Fri, 22 Jun 2018 15:49:08 -0400 Message-Id: <1529696948-16991-3-git-send-email-agustinv@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1529696948-16991-1-git-send-email-agustinv@codeaurora.org> References: <1529696948-16991-1-git-send-email-agustinv@codeaurora.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180622_204933_417425_E2B9AD7D X-CRM114-Status: GOOD ( 38.24 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: timur@codeaurora.org, agustinv@codeaurora.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Selection of these events can be envisioned as indexing them from a 3D matrix: - the first index selects a Region Event Selection Register (PMRESRx_EL0) - the second index selects a group from which only one event at a time can be selected - the third index selects the event These events are encoded into perf_event_attr as: mbe [config1:0 ] (flag that indicates a matrix-based event) reg [config:12-15] (specifies the PMRESRx_EL0 instance) group [config:0-3 ] (specifies the event group) code [config:4-11 ] (specifies the event) Events with the mbe flag set to zero are treated as common or raw PMUv3 events and are handled by the base PMUv3 driver code. The first two indexes are set combining the RESR and group number with a base number and writing it into the architected PMXEVTYPER_EL0 register. The third index is set by writing the code into the bits corresponding with the group into the appropriate IMPLEMENTATION DEFINED PMRESRx_EL0 register. Support for this extension is signaled by the presence of the Falkor PMU device node under each Falkor CPU device node in the DSDT ACPI table. E.g.: Device (CPU0) { Name (_HID, "ACPI0007" /* Processor Device */) ... Device (PMU0) { Name (_HID, "QCOM8150") /* Qualcomm Falkor PMU device */ ... } } Signed-off-by: Agustin Vega-Frias --- drivers/perf/Makefile | 2 +- drivers/perf/qcom_arm_pmu.c | 342 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 drivers/perf/qcom_arm_pmu.c diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile index b3902bd..a61afd9 100644 --- a/drivers/perf/Makefile +++ b/drivers/perf/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_ARM_CCI_PMU) += arm-cci.o obj-$(CONFIG_ARM_CCN) += arm-ccn.o obj-$(CONFIG_ARM_DSU_PMU) += arm_dsu_pmu.o obj-$(CONFIG_ARM_PMU) += arm_pmu.o arm_pmu_platform.o -obj-$(CONFIG_ARM_PMU_ACPI) += arm_pmu_acpi.o +obj-$(CONFIG_ARM_PMU_ACPI) += arm_pmu_acpi.o qcom_arm_pmu.o obj-$(CONFIG_HISI_PMU) += hisilicon/ obj-$(CONFIG_QCOM_L2_PMU) += qcom_l2_pmu.o obj-$(CONFIG_QCOM_L3_PMU) += qcom_l3_pmu.o diff --git a/drivers/perf/qcom_arm_pmu.c b/drivers/perf/qcom_arm_pmu.c new file mode 100644 index 0000000..2f5e736 --- /dev/null +++ b/drivers/perf/qcom_arm_pmu.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * Qualcomm Technologies CPU PMU IMPLEMENTATION DEFINED extensions support + * + * Current extensions supported: + * + * - Matrix-based microarchitectural events support + * + * Selection of these events can be envisioned as indexing them from + * a 3D matrix: + * - the first index selects a Region Event Selection Register (PMRESRx_EL0) + * - the second index selects a group from which only one event at a time + * can be selected + * - the third index selects the event + * + * These events are encoded into perf_event_attr as: + * mbe [config1:0 ] (flag that indicates a matrix-based event) + * reg [config:12-15] (specifies the PMRESRx_EL0 instance) + * group [config:0-3 ] (specifies the event group) + * code [config:4-11 ] (specifies the event) + * + * Events with the mbe flag set to zero are treated as common or raw PMUv3 + * events and are handled by the base PMUv3 driver code. + * + * The first two indexes are set combining the RESR and group number with a + * base number and writing it into the architected PMXEVTYPER_EL0.evtCount. + * The third index is set by writing the code into the bits corresponding + * with the group into the appropriate IMPLEMENTATION DEFINED PMRESRx_EL0 + * register. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define pmresr0_el0 sys_reg(3, 5, 11, 3, 0) +#define pmresr1_el0 sys_reg(3, 5, 11, 3, 2) +#define pmresr2_el0 sys_reg(3, 5, 11, 3, 4) +#define pmxevcntcr_el0 sys_reg(3, 5, 11, 0, 3) + +#define QC_EVT_MBE_SHIFT 0 +#define QC_EVT_REG_SHIFT 12 +#define QC_EVT_CODE_SHIFT 4 +#define QC_EVT_GRP_SHIFT 0 +#define QC_EVT_MBE_MASK GENMASK(QC_EVT_MBE_SHIFT + 1, QC_EVT_MBE_SHIFT) +#define QC_EVT_REG_MASK GENMASK(QC_EVT_REG_SHIFT + 3, QC_EVT_REG_SHIFT) +#define QC_EVT_CODE_MASK GENMASK(QC_EVT_CODE_SHIFT + 7, QC_EVT_CODE_SHIFT) +#define QC_EVT_GRP_MASK GENMASK(QC_EVT_GRP_SHIFT + 3, QC_EVT_GRP_SHIFT) +#define QC_EVT_RG_MASK (QC_EVT_REG_MASK | QC_EVT_GRP_MASK) +#define QC_EVT_RG(event) ((event)->attr.config & QC_EVT_RG_MASK) +#define QC_EVT_MBE(event) \ + (((event)->attr.config1 & QC_EVT_MBE_MASK) >> QC_EVT_MBE_SHIFT) +#define QC_EVT_REG(event) \ + (((event)->attr.config & QC_EVT_REG_MASK) >> QC_EVT_REG_SHIFT) +#define QC_EVT_CODE(event) \ + (((event)->attr.config & QC_EVT_CODE_MASK) >> QC_EVT_CODE_SHIFT) +#define QC_EVT_GROUP(event) \ + (((event)->attr.config & QC_EVT_GRP_MASK) >> QC_EVT_GRP_SHIFT) + +#define QC_MAX_GROUP 7 +#define QC_MAX_RESR 2 +#define QC_BITS_PER_GROUP 8 +#define QC_RESR_ENABLE BIT_ULL(63) +#define QC_RESR_EVT_BASE 0xd8 + +static struct arm_pmu *def_ops; + +static inline void falkor_write_pmresr(u64 reg, u64 val) +{ + switch (reg) { + case 0: + write_sysreg_s(val, pmresr0_el0); + return; + case 1: + write_sysreg_s(val, pmresr1_el0); + return; + default: + write_sysreg_s(val, pmresr2_el0); + return; + } +} + +static inline u64 falkor_read_pmresr(u64 reg) +{ + switch (reg) { + case 0: + return read_sysreg_s(pmresr0_el0); + case 1: + return read_sysreg_s(pmresr1_el0); + default: + return read_sysreg_s(pmresr2_el0); + } +} + +static void falkor_set_resr(u64 reg, u64 group, u64 code) +{ + u64 shift = group * QC_BITS_PER_GROUP; + u64 mask = GENMASK(shift + QC_BITS_PER_GROUP - 1, shift); + u64 val; + + val = falkor_read_pmresr(reg) & ~mask; + val |= (code << shift); + val |= QC_RESR_ENABLE; + falkor_write_pmresr(reg, val); +} + +static void falkor_clear_resr(u64 reg, u64 group) +{ + u32 shift = group * QC_BITS_PER_GROUP; + u64 mask = GENMASK(shift + QC_BITS_PER_GROUP - 1, shift); + u64 val = falkor_read_pmresr(reg) & ~mask; + + falkor_write_pmresr(reg, val == QC_RESR_ENABLE ? 0 : val); +} + +/* + * Check if e1 and e2 conflict with each other + * + * e1 is a matrix-based microarchitectural event we are checking against e2. + * A conflict exists if the events use the same reg, group, and a different + * code. + */ +static inline bool events_conflict(struct perf_event *e1, struct perf_event *e2) +{ + int type = e2->attr.type; + int dynamic = e1->pmu->type; + + /* Same event? */ + if (e1 == e2) + return false; + + /* Other PMU that is not the RAW or this PMU's dynamic type? */ + if ((e1->pmu != e2->pmu) && ((type != PERF_TYPE_RAW) && (type != dynamic))) + return false; + + /* No conflict if using different mbe */ + if (QC_EVT_MBE(e1) != QC_EVT_MBE(e2)) + return false; + + /* No conflict if using different reg or group */ + if (QC_EVT_RG(e1) != QC_EVT_RG(e2)) + return false; + + /* Same mbe, reg and group is fine so long as code matches */ + if (QC_EVT_CODE(e1) == QC_EVT_CODE(e2)) + return false; + + pr_debug_ratelimited("Group exclusion: conflicting events %llx %llx\n", + e1->attr.config, + e2->attr.config); + return true; +} + +/* + * Check if the given event is valid for the PMU and if so return the value + * that can be used in PMXEVTYPER_EL0 to select the event + */ +static int falkor_map_event(struct perf_event *event) +{ + int type = event->attr.type; + int dynamic = event->pmu->type; + u64 reg = QC_EVT_REG(event); + u64 group = QC_EVT_GROUP(event); + struct perf_event *leader; + struct perf_event *sibling; + + if (((type != PERF_TYPE_RAW) && (type != dynamic)) || !QC_EVT_MBE(event)) + /* Common PMUv3 event, forward to the original op */ + return def_ops->map_event(event); + + /* Is it a valid matrix event? */ + if ((group > QC_MAX_GROUP) || (reg > QC_MAX_RESR)) + return -ENOENT; + + /* If part of an event group, check if the event can be put in it */ + + leader = event->group_leader; + if (events_conflict(event, leader)) + return -ENOENT; + + for_each_sibling_event(sibling, leader) + if (events_conflict(event, sibling)) + return -ENOENT; + + return QC_RESR_EVT_BASE + reg * 8 + group; +} + +/* + * Find a slot for the event on the current CPU + */ +static int falkor_get_event_idx(struct pmu_hw_events *cpuc, struct perf_event *event) +{ + int type = event->attr.type; + int dynamic = event->pmu->type; + int idx; + + if (((type == PERF_TYPE_RAW) || (type == dynamic)) && QC_EVT_MBE(event)) + /* Matrix event, check for conflicts with existing events */ + for_each_set_bit(idx, cpuc->used_mask, ARMPMU_MAX_HWEVENTS) + if (cpuc->events[idx] && + events_conflict(event, cpuc->events[idx])) + return -ENOENT; + + /* Let the original op handle the rest */ + idx = def_ops->get_event_idx(cpuc, event); + + /* + * This is called for actually allocating the events, but also with + * a dummy pmu_hw_events when validating groups, for that case we + * need to ensure that cpuc->events[idx] is NULL so we don't use + * an uninitialized pointer. Conflicts for matrix events in groups + * are checked during event mapping anyway (see falkor_event_map). + */ + cpuc->events[idx] = NULL; + + return idx; +} + +/* + * Reset the PMU + */ +static void falkor_reset(void *info) +{ + struct arm_pmu *pmu = (struct arm_pmu *)info; + u32 i, ctrs = pmu->num_events; + + /* PMRESRx_EL0 regs are unknown at reset, except for the EN field */ + for (i = 0; i <= QC_MAX_RESR; i++) + falkor_write_pmresr(i, 0); + + /* PMXEVCNTCRx_EL0 regs are unknown at reset */ + for (i = 0; i <= ctrs; i++) { + write_sysreg(i, pmselr_el0); + isb(); + write_sysreg_s(0, pmxevcntcr_el0); + } + + /* Let the original op handle the rest */ + def_ops->reset(info); +} + +/* + * Enable the given event + */ +static void falkor_enable(struct perf_event *event) +{ + if (QC_EVT_MBE(event)) { + /* Matrix event, program the appropriate PMRESRx_EL0 */ + u64 reg = QC_EVT_REG(event); + u64 code = QC_EVT_CODE(event); + u64 group = QC_EVT_GROUP(event); + + falkor_set_resr(reg, group, code); + } + + /* Let the original op handle the rest */ + def_ops->enable(event); +} + +/* + * Disable the given event + */ +static void falkor_disable(struct perf_event *event) +{ + /* Use the original op to disable the counter and interrupt */ + def_ops->enable(event); + + if (QC_EVT_MBE(event)) { + /* Matrix event, de-program the appropriate PMRESRx_EL0 */ + u64 reg = QC_EVT_REG(event); + u64 group = QC_EVT_GROUP(event); + + falkor_clear_resr(reg, group); + } +} + +PMU_FORMAT_ATTR(event, "config:0-15"); +PMU_FORMAT_ATTR(mbe, "config1:0"); +PMU_FORMAT_ATTR(reg, "config:12-15"); +PMU_FORMAT_ATTR(code, "config:4-11"); +PMU_FORMAT_ATTR(group, "config:0-3"); + +static struct attribute *falkor_pmu_formats[] = { + &format_attr_event.attr, + &format_attr_mbe.attr, + &format_attr_reg.attr, + &format_attr_code.attr, + &format_attr_group.attr, + NULL, +}; + +static struct attribute_group falkor_pmu_format_attr_group = { + .name = "format", + .attrs = falkor_pmu_formats, +}; + +static int qcom_falkor_pmu_init(struct arm_pmu *pmu, struct device *dev) +{ + /* Save base arm_pmu so we can invoke its ops when appropriate */ + def_ops = devm_kmemdup(dev, pmu, sizeof(*def_ops), GFP_KERNEL); + if (!def_ops) { + pr_warn("Failed to allocate arm_pmu for QCOM extensions"); + return -ENODEV; + } + + pmu->name = "qcom_pmuv3"; + + /* Override the necessary ops */ + pmu->map_event = falkor_map_event; + pmu->get_event_idx = falkor_get_event_idx; + pmu->reset = falkor_reset; + pmu->enable = falkor_enable; + pmu->disable = falkor_disable; + + /* Override the necessary attributes */ + pmu->pmu.attr_groups[ARMPMU_ATTR_GROUP_FORMATS] = + &falkor_pmu_format_attr_group; + + return 1; +} + +ACPI_DECLARE_PMU_VARIANT(qcom_falkor, "QCOM8150", qcom_falkor_pmu_init);