162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Perf support for the Statistical Profiling Extension, introduced as 462306a36Sopenharmony_ci * part of ARMv8.2. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2016 ARM Limited 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Will Deacon <will.deacon@arm.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define PMUNAME "arm_spe" 1262306a36Sopenharmony_ci#define DRVNAME PMUNAME "_pmu" 1362306a36Sopenharmony_ci#define pr_fmt(fmt) DRVNAME ": " fmt 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/bitfield.h> 1662306a36Sopenharmony_ci#include <linux/bitops.h> 1762306a36Sopenharmony_ci#include <linux/bug.h> 1862306a36Sopenharmony_ci#include <linux/capability.h> 1962306a36Sopenharmony_ci#include <linux/cpuhotplug.h> 2062306a36Sopenharmony_ci#include <linux/cpumask.h> 2162306a36Sopenharmony_ci#include <linux/device.h> 2262306a36Sopenharmony_ci#include <linux/errno.h> 2362306a36Sopenharmony_ci#include <linux/interrupt.h> 2462306a36Sopenharmony_ci#include <linux/irq.h> 2562306a36Sopenharmony_ci#include <linux/kernel.h> 2662306a36Sopenharmony_ci#include <linux/list.h> 2762306a36Sopenharmony_ci#include <linux/module.h> 2862306a36Sopenharmony_ci#include <linux/of.h> 2962306a36Sopenharmony_ci#include <linux/perf_event.h> 3062306a36Sopenharmony_ci#include <linux/perf/arm_pmu.h> 3162306a36Sopenharmony_ci#include <linux/platform_device.h> 3262306a36Sopenharmony_ci#include <linux/printk.h> 3362306a36Sopenharmony_ci#include <linux/slab.h> 3462306a36Sopenharmony_ci#include <linux/smp.h> 3562306a36Sopenharmony_ci#include <linux/vmalloc.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <asm/barrier.h> 3862306a36Sopenharmony_ci#include <asm/cpufeature.h> 3962306a36Sopenharmony_ci#include <asm/mmu.h> 4062306a36Sopenharmony_ci#include <asm/sysreg.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Cache if the event is allowed to trace Context information. 4462306a36Sopenharmony_ci * This allows us to perform the check, i.e, perfmon_capable(), 4562306a36Sopenharmony_ci * in the context of the event owner, once, during the event_init(). 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci#define SPE_PMU_HW_FLAGS_CX 0x00001 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic_assert((PERF_EVENT_FLAG_ARCH & SPE_PMU_HW_FLAGS_CX) == SPE_PMU_HW_FLAGS_CX); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void set_spe_event_has_cx(struct perf_event *event) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PID_IN_CONTEXTIDR) && perfmon_capable()) 5462306a36Sopenharmony_ci event->hw.flags |= SPE_PMU_HW_FLAGS_CX; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic bool get_spe_event_has_cx(struct perf_event *event) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci return !!(event->hw.flags & SPE_PMU_HW_FLAGS_CX); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define ARM_SPE_BUF_PAD_BYTE 0 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistruct arm_spe_pmu_buf { 6562306a36Sopenharmony_ci int nr_pages; 6662306a36Sopenharmony_ci bool snapshot; 6762306a36Sopenharmony_ci void *base; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct arm_spe_pmu { 7162306a36Sopenharmony_ci struct pmu pmu; 7262306a36Sopenharmony_ci struct platform_device *pdev; 7362306a36Sopenharmony_ci cpumask_t supported_cpus; 7462306a36Sopenharmony_ci struct hlist_node hotplug_node; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci int irq; /* PPI */ 7762306a36Sopenharmony_ci u16 pmsver; 7862306a36Sopenharmony_ci u16 min_period; 7962306a36Sopenharmony_ci u16 counter_sz; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define SPE_PMU_FEAT_FILT_EVT (1UL << 0) 8262306a36Sopenharmony_ci#define SPE_PMU_FEAT_FILT_TYP (1UL << 1) 8362306a36Sopenharmony_ci#define SPE_PMU_FEAT_FILT_LAT (1UL << 2) 8462306a36Sopenharmony_ci#define SPE_PMU_FEAT_ARCH_INST (1UL << 3) 8562306a36Sopenharmony_ci#define SPE_PMU_FEAT_LDS (1UL << 4) 8662306a36Sopenharmony_ci#define SPE_PMU_FEAT_ERND (1UL << 5) 8762306a36Sopenharmony_ci#define SPE_PMU_FEAT_INV_FILT_EVT (1UL << 6) 8862306a36Sopenharmony_ci#define SPE_PMU_FEAT_DEV_PROBED (1UL << 63) 8962306a36Sopenharmony_ci u64 features; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci u16 max_record_sz; 9262306a36Sopenharmony_ci u16 align; 9362306a36Sopenharmony_ci struct perf_output_handle __percpu *handle; 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define to_spe_pmu(p) (container_of(p, struct arm_spe_pmu, pmu)) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* Convert a free-running index from perf into an SPE buffer offset */ 9962306a36Sopenharmony_ci#define PERF_IDX2OFF(idx, buf) ((idx) % ((buf)->nr_pages << PAGE_SHIFT)) 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* Keep track of our dynamic hotplug state */ 10262306a36Sopenharmony_cistatic enum cpuhp_state arm_spe_pmu_online; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cienum arm_spe_pmu_buf_fault_action { 10562306a36Sopenharmony_ci SPE_PMU_BUF_FAULT_ACT_SPURIOUS, 10662306a36Sopenharmony_ci SPE_PMU_BUF_FAULT_ACT_FATAL, 10762306a36Sopenharmony_ci SPE_PMU_BUF_FAULT_ACT_OK, 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* This sysfs gunk was really good fun to write. */ 11162306a36Sopenharmony_cienum arm_spe_pmu_capabilities { 11262306a36Sopenharmony_ci SPE_PMU_CAP_ARCH_INST = 0, 11362306a36Sopenharmony_ci SPE_PMU_CAP_ERND, 11462306a36Sopenharmony_ci SPE_PMU_CAP_FEAT_MAX, 11562306a36Sopenharmony_ci SPE_PMU_CAP_CNT_SZ = SPE_PMU_CAP_FEAT_MAX, 11662306a36Sopenharmony_ci SPE_PMU_CAP_MIN_IVAL, 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int arm_spe_pmu_feat_caps[SPE_PMU_CAP_FEAT_MAX] = { 12062306a36Sopenharmony_ci [SPE_PMU_CAP_ARCH_INST] = SPE_PMU_FEAT_ARCH_INST, 12162306a36Sopenharmony_ci [SPE_PMU_CAP_ERND] = SPE_PMU_FEAT_ERND, 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic u32 arm_spe_pmu_cap_get(struct arm_spe_pmu *spe_pmu, int cap) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci if (cap < SPE_PMU_CAP_FEAT_MAX) 12762306a36Sopenharmony_ci return !!(spe_pmu->features & arm_spe_pmu_feat_caps[cap]); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci switch (cap) { 13062306a36Sopenharmony_ci case SPE_PMU_CAP_CNT_SZ: 13162306a36Sopenharmony_ci return spe_pmu->counter_sz; 13262306a36Sopenharmony_ci case SPE_PMU_CAP_MIN_IVAL: 13362306a36Sopenharmony_ci return spe_pmu->min_period; 13462306a36Sopenharmony_ci default: 13562306a36Sopenharmony_ci WARN(1, "unknown cap %d\n", cap); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic ssize_t arm_spe_pmu_cap_show(struct device *dev, 14262306a36Sopenharmony_ci struct device_attribute *attr, 14362306a36Sopenharmony_ci char *buf) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = dev_get_drvdata(dev); 14662306a36Sopenharmony_ci struct dev_ext_attribute *ea = 14762306a36Sopenharmony_ci container_of(attr, struct dev_ext_attribute, attr); 14862306a36Sopenharmony_ci int cap = (long)ea->var; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", arm_spe_pmu_cap_get(spe_pmu, cap)); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci#define SPE_EXT_ATTR_ENTRY(_name, _func, _var) \ 15462306a36Sopenharmony_ci &((struct dev_ext_attribute[]) { \ 15562306a36Sopenharmony_ci { __ATTR(_name, S_IRUGO, _func, NULL), (void *)_var } \ 15662306a36Sopenharmony_ci })[0].attr.attr 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#define SPE_CAP_EXT_ATTR_ENTRY(_name, _var) \ 15962306a36Sopenharmony_ci SPE_EXT_ATTR_ENTRY(_name, arm_spe_pmu_cap_show, _var) 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic struct attribute *arm_spe_pmu_cap_attr[] = { 16262306a36Sopenharmony_ci SPE_CAP_EXT_ATTR_ENTRY(arch_inst, SPE_PMU_CAP_ARCH_INST), 16362306a36Sopenharmony_ci SPE_CAP_EXT_ATTR_ENTRY(ernd, SPE_PMU_CAP_ERND), 16462306a36Sopenharmony_ci SPE_CAP_EXT_ATTR_ENTRY(count_size, SPE_PMU_CAP_CNT_SZ), 16562306a36Sopenharmony_ci SPE_CAP_EXT_ATTR_ENTRY(min_interval, SPE_PMU_CAP_MIN_IVAL), 16662306a36Sopenharmony_ci NULL, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic const struct attribute_group arm_spe_pmu_cap_group = { 17062306a36Sopenharmony_ci .name = "caps", 17162306a36Sopenharmony_ci .attrs = arm_spe_pmu_cap_attr, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* User ABI */ 17562306a36Sopenharmony_ci#define ATTR_CFG_FLD_ts_enable_CFG config /* PMSCR_EL1.TS */ 17662306a36Sopenharmony_ci#define ATTR_CFG_FLD_ts_enable_LO 0 17762306a36Sopenharmony_ci#define ATTR_CFG_FLD_ts_enable_HI 0 17862306a36Sopenharmony_ci#define ATTR_CFG_FLD_pa_enable_CFG config /* PMSCR_EL1.PA */ 17962306a36Sopenharmony_ci#define ATTR_CFG_FLD_pa_enable_LO 1 18062306a36Sopenharmony_ci#define ATTR_CFG_FLD_pa_enable_HI 1 18162306a36Sopenharmony_ci#define ATTR_CFG_FLD_pct_enable_CFG config /* PMSCR_EL1.PCT */ 18262306a36Sopenharmony_ci#define ATTR_CFG_FLD_pct_enable_LO 2 18362306a36Sopenharmony_ci#define ATTR_CFG_FLD_pct_enable_HI 2 18462306a36Sopenharmony_ci#define ATTR_CFG_FLD_jitter_CFG config /* PMSIRR_EL1.RND */ 18562306a36Sopenharmony_ci#define ATTR_CFG_FLD_jitter_LO 16 18662306a36Sopenharmony_ci#define ATTR_CFG_FLD_jitter_HI 16 18762306a36Sopenharmony_ci#define ATTR_CFG_FLD_branch_filter_CFG config /* PMSFCR_EL1.B */ 18862306a36Sopenharmony_ci#define ATTR_CFG_FLD_branch_filter_LO 32 18962306a36Sopenharmony_ci#define ATTR_CFG_FLD_branch_filter_HI 32 19062306a36Sopenharmony_ci#define ATTR_CFG_FLD_load_filter_CFG config /* PMSFCR_EL1.LD */ 19162306a36Sopenharmony_ci#define ATTR_CFG_FLD_load_filter_LO 33 19262306a36Sopenharmony_ci#define ATTR_CFG_FLD_load_filter_HI 33 19362306a36Sopenharmony_ci#define ATTR_CFG_FLD_store_filter_CFG config /* PMSFCR_EL1.ST */ 19462306a36Sopenharmony_ci#define ATTR_CFG_FLD_store_filter_LO 34 19562306a36Sopenharmony_ci#define ATTR_CFG_FLD_store_filter_HI 34 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci#define ATTR_CFG_FLD_event_filter_CFG config1 /* PMSEVFR_EL1 */ 19862306a36Sopenharmony_ci#define ATTR_CFG_FLD_event_filter_LO 0 19962306a36Sopenharmony_ci#define ATTR_CFG_FLD_event_filter_HI 63 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci#define ATTR_CFG_FLD_min_latency_CFG config2 /* PMSLATFR_EL1.MINLAT */ 20262306a36Sopenharmony_ci#define ATTR_CFG_FLD_min_latency_LO 0 20362306a36Sopenharmony_ci#define ATTR_CFG_FLD_min_latency_HI 11 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci#define ATTR_CFG_FLD_inv_event_filter_CFG config3 /* PMSNEVFR_EL1 */ 20662306a36Sopenharmony_ci#define ATTR_CFG_FLD_inv_event_filter_LO 0 20762306a36Sopenharmony_ci#define ATTR_CFG_FLD_inv_event_filter_HI 63 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* Why does everything I do descend into this? */ 21062306a36Sopenharmony_ci#define __GEN_PMU_FORMAT_ATTR(cfg, lo, hi) \ 21162306a36Sopenharmony_ci (lo) == (hi) ? #cfg ":" #lo "\n" : #cfg ":" #lo "-" #hi 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci#define _GEN_PMU_FORMAT_ATTR(cfg, lo, hi) \ 21462306a36Sopenharmony_ci __GEN_PMU_FORMAT_ATTR(cfg, lo, hi) 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci#define GEN_PMU_FORMAT_ATTR(name) \ 21762306a36Sopenharmony_ci PMU_FORMAT_ATTR(name, \ 21862306a36Sopenharmony_ci _GEN_PMU_FORMAT_ATTR(ATTR_CFG_FLD_##name##_CFG, \ 21962306a36Sopenharmony_ci ATTR_CFG_FLD_##name##_LO, \ 22062306a36Sopenharmony_ci ATTR_CFG_FLD_##name##_HI)) 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci#define _ATTR_CFG_GET_FLD(attr, cfg, lo, hi) \ 22362306a36Sopenharmony_ci ((((attr)->cfg) >> lo) & GENMASK(hi - lo, 0)) 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci#define ATTR_CFG_GET_FLD(attr, name) \ 22662306a36Sopenharmony_ci _ATTR_CFG_GET_FLD(attr, \ 22762306a36Sopenharmony_ci ATTR_CFG_FLD_##name##_CFG, \ 22862306a36Sopenharmony_ci ATTR_CFG_FLD_##name##_LO, \ 22962306a36Sopenharmony_ci ATTR_CFG_FLD_##name##_HI) 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(ts_enable); 23262306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(pa_enable); 23362306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(pct_enable); 23462306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(jitter); 23562306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(branch_filter); 23662306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(load_filter); 23762306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(store_filter); 23862306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(event_filter); 23962306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(inv_event_filter); 24062306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(min_latency); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic struct attribute *arm_spe_pmu_formats_attr[] = { 24362306a36Sopenharmony_ci &format_attr_ts_enable.attr, 24462306a36Sopenharmony_ci &format_attr_pa_enable.attr, 24562306a36Sopenharmony_ci &format_attr_pct_enable.attr, 24662306a36Sopenharmony_ci &format_attr_jitter.attr, 24762306a36Sopenharmony_ci &format_attr_branch_filter.attr, 24862306a36Sopenharmony_ci &format_attr_load_filter.attr, 24962306a36Sopenharmony_ci &format_attr_store_filter.attr, 25062306a36Sopenharmony_ci &format_attr_event_filter.attr, 25162306a36Sopenharmony_ci &format_attr_inv_event_filter.attr, 25262306a36Sopenharmony_ci &format_attr_min_latency.attr, 25362306a36Sopenharmony_ci NULL, 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic umode_t arm_spe_pmu_format_attr_is_visible(struct kobject *kobj, 25762306a36Sopenharmony_ci struct attribute *attr, 25862306a36Sopenharmony_ci int unused) 25962306a36Sopenharmony_ci { 26062306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 26162306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = dev_get_drvdata(dev); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (attr == &format_attr_inv_event_filter.attr && !(spe_pmu->features & SPE_PMU_FEAT_INV_FILT_EVT)) 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return attr->mode; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic const struct attribute_group arm_spe_pmu_format_group = { 27062306a36Sopenharmony_ci .name = "format", 27162306a36Sopenharmony_ci .is_visible = arm_spe_pmu_format_attr_is_visible, 27262306a36Sopenharmony_ci .attrs = arm_spe_pmu_formats_attr, 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic ssize_t cpumask_show(struct device *dev, 27662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = dev_get_drvdata(dev); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, &spe_pmu->supported_cpus); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(cpumask); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic struct attribute *arm_spe_pmu_attrs[] = { 28562306a36Sopenharmony_ci &dev_attr_cpumask.attr, 28662306a36Sopenharmony_ci NULL, 28762306a36Sopenharmony_ci}; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic const struct attribute_group arm_spe_pmu_group = { 29062306a36Sopenharmony_ci .attrs = arm_spe_pmu_attrs, 29162306a36Sopenharmony_ci}; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic const struct attribute_group *arm_spe_pmu_attr_groups[] = { 29462306a36Sopenharmony_ci &arm_spe_pmu_group, 29562306a36Sopenharmony_ci &arm_spe_pmu_cap_group, 29662306a36Sopenharmony_ci &arm_spe_pmu_format_group, 29762306a36Sopenharmony_ci NULL, 29862306a36Sopenharmony_ci}; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/* Convert between user ABI and register values */ 30162306a36Sopenharmony_cistatic u64 arm_spe_event_to_pmscr(struct perf_event *event) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct perf_event_attr *attr = &event->attr; 30462306a36Sopenharmony_ci u64 reg = 0; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci reg |= FIELD_PREP(PMSCR_EL1_TS, ATTR_CFG_GET_FLD(attr, ts_enable)); 30762306a36Sopenharmony_ci reg |= FIELD_PREP(PMSCR_EL1_PA, ATTR_CFG_GET_FLD(attr, pa_enable)); 30862306a36Sopenharmony_ci reg |= FIELD_PREP(PMSCR_EL1_PCT, ATTR_CFG_GET_FLD(attr, pct_enable)); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (!attr->exclude_user) 31162306a36Sopenharmony_ci reg |= PMSCR_EL1_E0SPE; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (!attr->exclude_kernel) 31462306a36Sopenharmony_ci reg |= PMSCR_EL1_E1SPE; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (get_spe_event_has_cx(event)) 31762306a36Sopenharmony_ci reg |= PMSCR_EL1_CX; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return reg; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void arm_spe_event_sanitise_period(struct perf_event *event) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = to_spe_pmu(event->pmu); 32562306a36Sopenharmony_ci u64 period = event->hw.sample_period; 32662306a36Sopenharmony_ci u64 max_period = PMSIRR_EL1_INTERVAL_MASK; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (period < spe_pmu->min_period) 32962306a36Sopenharmony_ci period = spe_pmu->min_period; 33062306a36Sopenharmony_ci else if (period > max_period) 33162306a36Sopenharmony_ci period = max_period; 33262306a36Sopenharmony_ci else 33362306a36Sopenharmony_ci period &= max_period; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci event->hw.sample_period = period; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic u64 arm_spe_event_to_pmsirr(struct perf_event *event) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct perf_event_attr *attr = &event->attr; 34162306a36Sopenharmony_ci u64 reg = 0; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci arm_spe_event_sanitise_period(event); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci reg |= FIELD_PREP(PMSIRR_EL1_RND, ATTR_CFG_GET_FLD(attr, jitter)); 34662306a36Sopenharmony_ci reg |= event->hw.sample_period; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return reg; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic u64 arm_spe_event_to_pmsfcr(struct perf_event *event) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct perf_event_attr *attr = &event->attr; 35462306a36Sopenharmony_ci u64 reg = 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci reg |= FIELD_PREP(PMSFCR_EL1_LD, ATTR_CFG_GET_FLD(attr, load_filter)); 35762306a36Sopenharmony_ci reg |= FIELD_PREP(PMSFCR_EL1_ST, ATTR_CFG_GET_FLD(attr, store_filter)); 35862306a36Sopenharmony_ci reg |= FIELD_PREP(PMSFCR_EL1_B, ATTR_CFG_GET_FLD(attr, branch_filter)); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (reg) 36162306a36Sopenharmony_ci reg |= PMSFCR_EL1_FT; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (ATTR_CFG_GET_FLD(attr, event_filter)) 36462306a36Sopenharmony_ci reg |= PMSFCR_EL1_FE; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (ATTR_CFG_GET_FLD(attr, inv_event_filter)) 36762306a36Sopenharmony_ci reg |= PMSFCR_EL1_FnE; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (ATTR_CFG_GET_FLD(attr, min_latency)) 37062306a36Sopenharmony_ci reg |= PMSFCR_EL1_FL; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return reg; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic u64 arm_spe_event_to_pmsevfr(struct perf_event *event) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct perf_event_attr *attr = &event->attr; 37862306a36Sopenharmony_ci return ATTR_CFG_GET_FLD(attr, event_filter); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic u64 arm_spe_event_to_pmsnevfr(struct perf_event *event) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct perf_event_attr *attr = &event->attr; 38462306a36Sopenharmony_ci return ATTR_CFG_GET_FLD(attr, inv_event_filter); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic u64 arm_spe_event_to_pmslatfr(struct perf_event *event) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct perf_event_attr *attr = &event->attr; 39062306a36Sopenharmony_ci return FIELD_PREP(PMSLATFR_EL1_MINLAT, ATTR_CFG_GET_FLD(attr, min_latency)); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic void arm_spe_pmu_pad_buf(struct perf_output_handle *handle, int len) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct arm_spe_pmu_buf *buf = perf_get_aux(handle); 39662306a36Sopenharmony_ci u64 head = PERF_IDX2OFF(handle->head, buf); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci memset(buf->base + head, ARM_SPE_BUF_PAD_BYTE, len); 39962306a36Sopenharmony_ci if (!buf->snapshot) 40062306a36Sopenharmony_ci perf_aux_output_skip(handle, len); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic u64 arm_spe_pmu_next_snapshot_off(struct perf_output_handle *handle) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct arm_spe_pmu_buf *buf = perf_get_aux(handle); 40662306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = to_spe_pmu(handle->event->pmu); 40762306a36Sopenharmony_ci u64 head = PERF_IDX2OFF(handle->head, buf); 40862306a36Sopenharmony_ci u64 limit = buf->nr_pages * PAGE_SIZE; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* 41162306a36Sopenharmony_ci * The trace format isn't parseable in reverse, so clamp 41262306a36Sopenharmony_ci * the limit to half of the buffer size in snapshot mode 41362306a36Sopenharmony_ci * so that the worst case is half a buffer of records, as 41462306a36Sopenharmony_ci * opposed to a single record. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci if (head < limit >> 1) 41762306a36Sopenharmony_ci limit >>= 1; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci * If we're within max_record_sz of the limit, we must 42162306a36Sopenharmony_ci * pad, move the head index and recompute the limit. 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci if (limit - head < spe_pmu->max_record_sz) { 42462306a36Sopenharmony_ci arm_spe_pmu_pad_buf(handle, limit - head); 42562306a36Sopenharmony_ci handle->head = PERF_IDX2OFF(limit, buf); 42662306a36Sopenharmony_ci limit = ((buf->nr_pages * PAGE_SIZE) >> 1) + handle->head; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return limit; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic u64 __arm_spe_pmu_next_off(struct perf_output_handle *handle) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = to_spe_pmu(handle->event->pmu); 43562306a36Sopenharmony_ci struct arm_spe_pmu_buf *buf = perf_get_aux(handle); 43662306a36Sopenharmony_ci const u64 bufsize = buf->nr_pages * PAGE_SIZE; 43762306a36Sopenharmony_ci u64 limit = bufsize; 43862306a36Sopenharmony_ci u64 head, tail, wakeup; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* 44162306a36Sopenharmony_ci * The head can be misaligned for two reasons: 44262306a36Sopenharmony_ci * 44362306a36Sopenharmony_ci * 1. The hardware left PMBPTR pointing to the first byte after 44462306a36Sopenharmony_ci * a record when generating a buffer management event. 44562306a36Sopenharmony_ci * 44662306a36Sopenharmony_ci * 2. We used perf_aux_output_skip to consume handle->size bytes 44762306a36Sopenharmony_ci * and CIRC_SPACE was used to compute the size, which always 44862306a36Sopenharmony_ci * leaves one entry free. 44962306a36Sopenharmony_ci * 45062306a36Sopenharmony_ci * Deal with this by padding to the next alignment boundary and 45162306a36Sopenharmony_ci * moving the head index. If we run out of buffer space, we'll 45262306a36Sopenharmony_ci * reduce handle->size to zero and end up reporting truncation. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci head = PERF_IDX2OFF(handle->head, buf); 45562306a36Sopenharmony_ci if (!IS_ALIGNED(head, spe_pmu->align)) { 45662306a36Sopenharmony_ci unsigned long delta = roundup(head, spe_pmu->align) - head; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci delta = min(delta, handle->size); 45962306a36Sopenharmony_ci arm_spe_pmu_pad_buf(handle, delta); 46062306a36Sopenharmony_ci head = PERF_IDX2OFF(handle->head, buf); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* If we've run out of free space, then nothing more to do */ 46462306a36Sopenharmony_ci if (!handle->size) 46562306a36Sopenharmony_ci goto no_space; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Compute the tail and wakeup indices now that we've aligned head */ 46862306a36Sopenharmony_ci tail = PERF_IDX2OFF(handle->head + handle->size, buf); 46962306a36Sopenharmony_ci wakeup = PERF_IDX2OFF(handle->wakeup, buf); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* 47262306a36Sopenharmony_ci * Avoid clobbering unconsumed data. We know we have space, so 47362306a36Sopenharmony_ci * if we see head == tail we know that the buffer is empty. If 47462306a36Sopenharmony_ci * head > tail, then there's nothing to clobber prior to 47562306a36Sopenharmony_ci * wrapping. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci if (head < tail) 47862306a36Sopenharmony_ci limit = round_down(tail, PAGE_SIZE); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * Wakeup may be arbitrarily far into the future. If it's not in 48262306a36Sopenharmony_ci * the current generation, either we'll wrap before hitting it, 48362306a36Sopenharmony_ci * or it's in the past and has been handled already. 48462306a36Sopenharmony_ci * 48562306a36Sopenharmony_ci * If there's a wakeup before we wrap, arrange to be woken up by 48662306a36Sopenharmony_ci * the page boundary following it. Keep the tail boundary if 48762306a36Sopenharmony_ci * that's lower. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci if (handle->wakeup < (handle->head + handle->size) && head <= wakeup) 49062306a36Sopenharmony_ci limit = min(limit, round_up(wakeup, PAGE_SIZE)); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (limit > head) 49362306a36Sopenharmony_ci return limit; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci arm_spe_pmu_pad_buf(handle, handle->size); 49662306a36Sopenharmony_cino_space: 49762306a36Sopenharmony_ci perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); 49862306a36Sopenharmony_ci perf_aux_output_end(handle, 0); 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic u64 arm_spe_pmu_next_off(struct perf_output_handle *handle) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct arm_spe_pmu_buf *buf = perf_get_aux(handle); 50562306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = to_spe_pmu(handle->event->pmu); 50662306a36Sopenharmony_ci u64 limit = __arm_spe_pmu_next_off(handle); 50762306a36Sopenharmony_ci u64 head = PERF_IDX2OFF(handle->head, buf); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* 51062306a36Sopenharmony_ci * If the head has come too close to the end of the buffer, 51162306a36Sopenharmony_ci * then pad to the end and recompute the limit. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci if (limit && (limit - head < spe_pmu->max_record_sz)) { 51462306a36Sopenharmony_ci arm_spe_pmu_pad_buf(handle, limit - head); 51562306a36Sopenharmony_ci limit = __arm_spe_pmu_next_off(handle); 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return limit; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic void arm_spe_perf_aux_output_begin(struct perf_output_handle *handle, 52262306a36Sopenharmony_ci struct perf_event *event) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci u64 base, limit; 52562306a36Sopenharmony_ci struct arm_spe_pmu_buf *buf; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Start a new aux session */ 52862306a36Sopenharmony_ci buf = perf_aux_output_begin(handle, event); 52962306a36Sopenharmony_ci if (!buf) { 53062306a36Sopenharmony_ci event->hw.state |= PERF_HES_STOPPED; 53162306a36Sopenharmony_ci /* 53262306a36Sopenharmony_ci * We still need to clear the limit pointer, since the 53362306a36Sopenharmony_ci * profiler might only be disabled by virtue of a fault. 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci limit = 0; 53662306a36Sopenharmony_ci goto out_write_limit; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci limit = buf->snapshot ? arm_spe_pmu_next_snapshot_off(handle) 54062306a36Sopenharmony_ci : arm_spe_pmu_next_off(handle); 54162306a36Sopenharmony_ci if (limit) 54262306a36Sopenharmony_ci limit |= PMBLIMITR_EL1_E; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci limit += (u64)buf->base; 54562306a36Sopenharmony_ci base = (u64)buf->base + PERF_IDX2OFF(handle->head, buf); 54662306a36Sopenharmony_ci write_sysreg_s(base, SYS_PMBPTR_EL1); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ciout_write_limit: 54962306a36Sopenharmony_ci write_sysreg_s(limit, SYS_PMBLIMITR_EL1); 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic void arm_spe_perf_aux_output_end(struct perf_output_handle *handle) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct arm_spe_pmu_buf *buf = perf_get_aux(handle); 55562306a36Sopenharmony_ci u64 offset, size; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci offset = read_sysreg_s(SYS_PMBPTR_EL1) - (u64)buf->base; 55862306a36Sopenharmony_ci size = offset - PERF_IDX2OFF(handle->head, buf); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (buf->snapshot) 56162306a36Sopenharmony_ci handle->head = offset; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci perf_aux_output_end(handle, size); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic void arm_spe_pmu_disable_and_drain_local(void) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci /* Disable profiling at EL0 and EL1 */ 56962306a36Sopenharmony_ci write_sysreg_s(0, SYS_PMSCR_EL1); 57062306a36Sopenharmony_ci isb(); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Drain any buffered data */ 57362306a36Sopenharmony_ci psb_csync(); 57462306a36Sopenharmony_ci dsb(nsh); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* Disable the profiling buffer */ 57762306a36Sopenharmony_ci write_sysreg_s(0, SYS_PMBLIMITR_EL1); 57862306a36Sopenharmony_ci isb(); 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci/* IRQ handling */ 58262306a36Sopenharmony_cistatic enum arm_spe_pmu_buf_fault_action 58362306a36Sopenharmony_ciarm_spe_pmu_buf_get_fault_act(struct perf_output_handle *handle) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci const char *err_str; 58662306a36Sopenharmony_ci u64 pmbsr; 58762306a36Sopenharmony_ci enum arm_spe_pmu_buf_fault_action ret; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* 59062306a36Sopenharmony_ci * Ensure new profiling data is visible to the CPU and any external 59162306a36Sopenharmony_ci * aborts have been resolved. 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci psb_csync(); 59462306a36Sopenharmony_ci dsb(nsh); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* Ensure hardware updates to PMBPTR_EL1 are visible */ 59762306a36Sopenharmony_ci isb(); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* Service required? */ 60062306a36Sopenharmony_ci pmbsr = read_sysreg_s(SYS_PMBSR_EL1); 60162306a36Sopenharmony_ci if (!FIELD_GET(PMBSR_EL1_S, pmbsr)) 60262306a36Sopenharmony_ci return SPE_PMU_BUF_FAULT_ACT_SPURIOUS; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* 60562306a36Sopenharmony_ci * If we've lost data, disable profiling and also set the PARTIAL 60662306a36Sopenharmony_ci * flag to indicate that the last record is corrupted. 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_ci if (FIELD_GET(PMBSR_EL1_DL, pmbsr)) 60962306a36Sopenharmony_ci perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED | 61062306a36Sopenharmony_ci PERF_AUX_FLAG_PARTIAL); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Report collisions to userspace so that it can up the period */ 61362306a36Sopenharmony_ci if (FIELD_GET(PMBSR_EL1_COLL, pmbsr)) 61462306a36Sopenharmony_ci perf_aux_output_flag(handle, PERF_AUX_FLAG_COLLISION); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* We only expect buffer management events */ 61762306a36Sopenharmony_ci switch (FIELD_GET(PMBSR_EL1_EC, pmbsr)) { 61862306a36Sopenharmony_ci case PMBSR_EL1_EC_BUF: 61962306a36Sopenharmony_ci /* Handled below */ 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci case PMBSR_EL1_EC_FAULT_S1: 62262306a36Sopenharmony_ci case PMBSR_EL1_EC_FAULT_S2: 62362306a36Sopenharmony_ci err_str = "Unexpected buffer fault"; 62462306a36Sopenharmony_ci goto out_err; 62562306a36Sopenharmony_ci default: 62662306a36Sopenharmony_ci err_str = "Unknown error code"; 62762306a36Sopenharmony_ci goto out_err; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci /* Buffer management event */ 63162306a36Sopenharmony_ci switch (FIELD_GET(PMBSR_EL1_BUF_BSC_MASK, pmbsr)) { 63262306a36Sopenharmony_ci case PMBSR_EL1_BUF_BSC_FULL: 63362306a36Sopenharmony_ci ret = SPE_PMU_BUF_FAULT_ACT_OK; 63462306a36Sopenharmony_ci goto out_stop; 63562306a36Sopenharmony_ci default: 63662306a36Sopenharmony_ci err_str = "Unknown buffer status code"; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ciout_err: 64062306a36Sopenharmony_ci pr_err_ratelimited("%s on CPU %d [PMBSR=0x%016llx, PMBPTR=0x%016llx, PMBLIMITR=0x%016llx]\n", 64162306a36Sopenharmony_ci err_str, smp_processor_id(), pmbsr, 64262306a36Sopenharmony_ci read_sysreg_s(SYS_PMBPTR_EL1), 64362306a36Sopenharmony_ci read_sysreg_s(SYS_PMBLIMITR_EL1)); 64462306a36Sopenharmony_ci ret = SPE_PMU_BUF_FAULT_ACT_FATAL; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ciout_stop: 64762306a36Sopenharmony_ci arm_spe_perf_aux_output_end(handle); 64862306a36Sopenharmony_ci return ret; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic irqreturn_t arm_spe_pmu_irq_handler(int irq, void *dev) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct perf_output_handle *handle = dev; 65462306a36Sopenharmony_ci struct perf_event *event = handle->event; 65562306a36Sopenharmony_ci enum arm_spe_pmu_buf_fault_action act; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (!perf_get_aux(handle)) 65862306a36Sopenharmony_ci return IRQ_NONE; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci act = arm_spe_pmu_buf_get_fault_act(handle); 66162306a36Sopenharmony_ci if (act == SPE_PMU_BUF_FAULT_ACT_SPURIOUS) 66262306a36Sopenharmony_ci return IRQ_NONE; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* 66562306a36Sopenharmony_ci * Ensure perf callbacks have completed, which may disable the 66662306a36Sopenharmony_ci * profiling buffer in response to a TRUNCATION flag. 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ci irq_work_run(); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci switch (act) { 67162306a36Sopenharmony_ci case SPE_PMU_BUF_FAULT_ACT_FATAL: 67262306a36Sopenharmony_ci /* 67362306a36Sopenharmony_ci * If a fatal exception occurred then leaving the profiling 67462306a36Sopenharmony_ci * buffer enabled is a recipe waiting to happen. Since 67562306a36Sopenharmony_ci * fatal faults don't always imply truncation, make sure 67662306a36Sopenharmony_ci * that the profiling buffer is disabled explicitly before 67762306a36Sopenharmony_ci * clearing the syndrome register. 67862306a36Sopenharmony_ci */ 67962306a36Sopenharmony_ci arm_spe_pmu_disable_and_drain_local(); 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci case SPE_PMU_BUF_FAULT_ACT_OK: 68262306a36Sopenharmony_ci /* 68362306a36Sopenharmony_ci * We handled the fault (the buffer was full), so resume 68462306a36Sopenharmony_ci * profiling as long as we didn't detect truncation. 68562306a36Sopenharmony_ci * PMBPTR might be misaligned, but we'll burn that bridge 68662306a36Sopenharmony_ci * when we get to it. 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_ci if (!(handle->aux_flags & PERF_AUX_FLAG_TRUNCATED)) { 68962306a36Sopenharmony_ci arm_spe_perf_aux_output_begin(handle, event); 69062306a36Sopenharmony_ci isb(); 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci break; 69362306a36Sopenharmony_ci case SPE_PMU_BUF_FAULT_ACT_SPURIOUS: 69462306a36Sopenharmony_ci /* We've seen you before, but GCC has the memory of a sieve. */ 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* The buffer pointers are now sane, so resume profiling. */ 69962306a36Sopenharmony_ci write_sysreg_s(0, SYS_PMBSR_EL1); 70062306a36Sopenharmony_ci return IRQ_HANDLED; 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic u64 arm_spe_pmsevfr_res0(u16 pmsver) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci switch (pmsver) { 70662306a36Sopenharmony_ci case ID_AA64DFR0_EL1_PMSVer_IMP: 70762306a36Sopenharmony_ci return PMSEVFR_EL1_RES0_IMP; 70862306a36Sopenharmony_ci case ID_AA64DFR0_EL1_PMSVer_V1P1: 70962306a36Sopenharmony_ci return PMSEVFR_EL1_RES0_V1P1; 71062306a36Sopenharmony_ci case ID_AA64DFR0_EL1_PMSVer_V1P2: 71162306a36Sopenharmony_ci /* Return the highest version we support in default */ 71262306a36Sopenharmony_ci default: 71362306a36Sopenharmony_ci return PMSEVFR_EL1_RES0_V1P2; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci/* Perf callbacks */ 71862306a36Sopenharmony_cistatic int arm_spe_pmu_event_init(struct perf_event *event) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci u64 reg; 72162306a36Sopenharmony_ci struct perf_event_attr *attr = &event->attr; 72262306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = to_spe_pmu(event->pmu); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* This is, of course, deeply driver-specific */ 72562306a36Sopenharmony_ci if (attr->type != event->pmu->type) 72662306a36Sopenharmony_ci return -ENOENT; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (event->cpu >= 0 && 72962306a36Sopenharmony_ci !cpumask_test_cpu(event->cpu, &spe_pmu->supported_cpus)) 73062306a36Sopenharmony_ci return -ENOENT; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (arm_spe_event_to_pmsevfr(event) & arm_spe_pmsevfr_res0(spe_pmu->pmsver)) 73362306a36Sopenharmony_ci return -EOPNOTSUPP; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (arm_spe_event_to_pmsnevfr(event) & arm_spe_pmsevfr_res0(spe_pmu->pmsver)) 73662306a36Sopenharmony_ci return -EOPNOTSUPP; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (attr->exclude_idle) 73962306a36Sopenharmony_ci return -EOPNOTSUPP; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* 74262306a36Sopenharmony_ci * Feedback-directed frequency throttling doesn't work when we 74362306a36Sopenharmony_ci * have a buffer of samples. We'd need to manually count the 74462306a36Sopenharmony_ci * samples in the buffer when it fills up and adjust the event 74562306a36Sopenharmony_ci * count to reflect that. Instead, just force the user to specify 74662306a36Sopenharmony_ci * a sample period. 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_ci if (attr->freq) 74962306a36Sopenharmony_ci return -EINVAL; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci reg = arm_spe_event_to_pmsfcr(event); 75262306a36Sopenharmony_ci if ((FIELD_GET(PMSFCR_EL1_FE, reg)) && 75362306a36Sopenharmony_ci !(spe_pmu->features & SPE_PMU_FEAT_FILT_EVT)) 75462306a36Sopenharmony_ci return -EOPNOTSUPP; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if ((FIELD_GET(PMSFCR_EL1_FnE, reg)) && 75762306a36Sopenharmony_ci !(spe_pmu->features & SPE_PMU_FEAT_INV_FILT_EVT)) 75862306a36Sopenharmony_ci return -EOPNOTSUPP; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if ((FIELD_GET(PMSFCR_EL1_FT, reg)) && 76162306a36Sopenharmony_ci !(spe_pmu->features & SPE_PMU_FEAT_FILT_TYP)) 76262306a36Sopenharmony_ci return -EOPNOTSUPP; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if ((FIELD_GET(PMSFCR_EL1_FL, reg)) && 76562306a36Sopenharmony_ci !(spe_pmu->features & SPE_PMU_FEAT_FILT_LAT)) 76662306a36Sopenharmony_ci return -EOPNOTSUPP; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci set_spe_event_has_cx(event); 76962306a36Sopenharmony_ci reg = arm_spe_event_to_pmscr(event); 77062306a36Sopenharmony_ci if (!perfmon_capable() && 77162306a36Sopenharmony_ci (reg & (PMSCR_EL1_PA | PMSCR_EL1_PCT))) 77262306a36Sopenharmony_ci return -EACCES; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci return 0; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic void arm_spe_pmu_start(struct perf_event *event, int flags) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci u64 reg; 78062306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = to_spe_pmu(event->pmu); 78162306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 78262306a36Sopenharmony_ci struct perf_output_handle *handle = this_cpu_ptr(spe_pmu->handle); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci hwc->state = 0; 78562306a36Sopenharmony_ci arm_spe_perf_aux_output_begin(handle, event); 78662306a36Sopenharmony_ci if (hwc->state) 78762306a36Sopenharmony_ci return; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci reg = arm_spe_event_to_pmsfcr(event); 79062306a36Sopenharmony_ci write_sysreg_s(reg, SYS_PMSFCR_EL1); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci reg = arm_spe_event_to_pmsevfr(event); 79362306a36Sopenharmony_ci write_sysreg_s(reg, SYS_PMSEVFR_EL1); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (spe_pmu->features & SPE_PMU_FEAT_INV_FILT_EVT) { 79662306a36Sopenharmony_ci reg = arm_spe_event_to_pmsnevfr(event); 79762306a36Sopenharmony_ci write_sysreg_s(reg, SYS_PMSNEVFR_EL1); 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci reg = arm_spe_event_to_pmslatfr(event); 80162306a36Sopenharmony_ci write_sysreg_s(reg, SYS_PMSLATFR_EL1); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (flags & PERF_EF_RELOAD) { 80462306a36Sopenharmony_ci reg = arm_spe_event_to_pmsirr(event); 80562306a36Sopenharmony_ci write_sysreg_s(reg, SYS_PMSIRR_EL1); 80662306a36Sopenharmony_ci isb(); 80762306a36Sopenharmony_ci reg = local64_read(&hwc->period_left); 80862306a36Sopenharmony_ci write_sysreg_s(reg, SYS_PMSICR_EL1); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci reg = arm_spe_event_to_pmscr(event); 81262306a36Sopenharmony_ci isb(); 81362306a36Sopenharmony_ci write_sysreg_s(reg, SYS_PMSCR_EL1); 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic void arm_spe_pmu_stop(struct perf_event *event, int flags) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = to_spe_pmu(event->pmu); 81962306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 82062306a36Sopenharmony_ci struct perf_output_handle *handle = this_cpu_ptr(spe_pmu->handle); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* If we're already stopped, then nothing to do */ 82362306a36Sopenharmony_ci if (hwc->state & PERF_HES_STOPPED) 82462306a36Sopenharmony_ci return; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* Stop all trace generation */ 82762306a36Sopenharmony_ci arm_spe_pmu_disable_and_drain_local(); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if (flags & PERF_EF_UPDATE) { 83062306a36Sopenharmony_ci /* 83162306a36Sopenharmony_ci * If there's a fault pending then ensure we contain it 83262306a36Sopenharmony_ci * to this buffer, since we might be on the context-switch 83362306a36Sopenharmony_ci * path. 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_ci if (perf_get_aux(handle)) { 83662306a36Sopenharmony_ci enum arm_spe_pmu_buf_fault_action act; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci act = arm_spe_pmu_buf_get_fault_act(handle); 83962306a36Sopenharmony_ci if (act == SPE_PMU_BUF_FAULT_ACT_SPURIOUS) 84062306a36Sopenharmony_ci arm_spe_perf_aux_output_end(handle); 84162306a36Sopenharmony_ci else 84262306a36Sopenharmony_ci write_sysreg_s(0, SYS_PMBSR_EL1); 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci /* 84662306a36Sopenharmony_ci * This may also contain ECOUNT, but nobody else should 84762306a36Sopenharmony_ci * be looking at period_left, since we forbid frequency 84862306a36Sopenharmony_ci * based sampling. 84962306a36Sopenharmony_ci */ 85062306a36Sopenharmony_ci local64_set(&hwc->period_left, read_sysreg_s(SYS_PMSICR_EL1)); 85162306a36Sopenharmony_ci hwc->state |= PERF_HES_UPTODATE; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci hwc->state |= PERF_HES_STOPPED; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic int arm_spe_pmu_add(struct perf_event *event, int flags) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci int ret = 0; 86062306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = to_spe_pmu(event->pmu); 86162306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 86262306a36Sopenharmony_ci int cpu = event->cpu == -1 ? smp_processor_id() : event->cpu; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (!cpumask_test_cpu(cpu, &spe_pmu->supported_cpus)) 86562306a36Sopenharmony_ci return -ENOENT; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (flags & PERF_EF_START) { 87062306a36Sopenharmony_ci arm_spe_pmu_start(event, PERF_EF_RELOAD); 87162306a36Sopenharmony_ci if (hwc->state & PERF_HES_STOPPED) 87262306a36Sopenharmony_ci ret = -EINVAL; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci return ret; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic void arm_spe_pmu_del(struct perf_event *event, int flags) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci arm_spe_pmu_stop(event, PERF_EF_UPDATE); 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic void arm_spe_pmu_read(struct perf_event *event) 88462306a36Sopenharmony_ci{ 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic void *arm_spe_pmu_setup_aux(struct perf_event *event, void **pages, 88862306a36Sopenharmony_ci int nr_pages, bool snapshot) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci int i, cpu = event->cpu; 89162306a36Sopenharmony_ci struct page **pglist; 89262306a36Sopenharmony_ci struct arm_spe_pmu_buf *buf; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* We need at least two pages for this to work. */ 89562306a36Sopenharmony_ci if (nr_pages < 2) 89662306a36Sopenharmony_ci return NULL; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci /* 89962306a36Sopenharmony_ci * We require an even number of pages for snapshot mode, so that 90062306a36Sopenharmony_ci * we can effectively treat the buffer as consisting of two equal 90162306a36Sopenharmony_ci * parts and give userspace a fighting chance of getting some 90262306a36Sopenharmony_ci * useful data out of it. 90362306a36Sopenharmony_ci */ 90462306a36Sopenharmony_ci if (snapshot && (nr_pages & 1)) 90562306a36Sopenharmony_ci return NULL; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (cpu == -1) 90862306a36Sopenharmony_ci cpu = raw_smp_processor_id(); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci buf = kzalloc_node(sizeof(*buf), GFP_KERNEL, cpu_to_node(cpu)); 91162306a36Sopenharmony_ci if (!buf) 91262306a36Sopenharmony_ci return NULL; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci pglist = kcalloc(nr_pages, sizeof(*pglist), GFP_KERNEL); 91562306a36Sopenharmony_ci if (!pglist) 91662306a36Sopenharmony_ci goto out_free_buf; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci for (i = 0; i < nr_pages; ++i) 91962306a36Sopenharmony_ci pglist[i] = virt_to_page(pages[i]); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci buf->base = vmap(pglist, nr_pages, VM_MAP, PAGE_KERNEL); 92262306a36Sopenharmony_ci if (!buf->base) 92362306a36Sopenharmony_ci goto out_free_pglist; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci buf->nr_pages = nr_pages; 92662306a36Sopenharmony_ci buf->snapshot = snapshot; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci kfree(pglist); 92962306a36Sopenharmony_ci return buf; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ciout_free_pglist: 93262306a36Sopenharmony_ci kfree(pglist); 93362306a36Sopenharmony_ciout_free_buf: 93462306a36Sopenharmony_ci kfree(buf); 93562306a36Sopenharmony_ci return NULL; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic void arm_spe_pmu_free_aux(void *aux) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci struct arm_spe_pmu_buf *buf = aux; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci vunmap(buf->base); 94362306a36Sopenharmony_ci kfree(buf); 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci/* Initialisation and teardown functions */ 94762306a36Sopenharmony_cistatic int arm_spe_pmu_perf_init(struct arm_spe_pmu *spe_pmu) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci static atomic_t pmu_idx = ATOMIC_INIT(-1); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci int idx; 95262306a36Sopenharmony_ci char *name; 95362306a36Sopenharmony_ci struct device *dev = &spe_pmu->pdev->dev; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci spe_pmu->pmu = (struct pmu) { 95662306a36Sopenharmony_ci .module = THIS_MODULE, 95762306a36Sopenharmony_ci .capabilities = PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_ITRACE, 95862306a36Sopenharmony_ci .attr_groups = arm_spe_pmu_attr_groups, 95962306a36Sopenharmony_ci /* 96062306a36Sopenharmony_ci * We hitch a ride on the software context here, so that 96162306a36Sopenharmony_ci * we can support per-task profiling (which is not possible 96262306a36Sopenharmony_ci * with the invalid context as it doesn't get sched callbacks). 96362306a36Sopenharmony_ci * This requires that userspace either uses a dummy event for 96462306a36Sopenharmony_ci * perf_event_open, since the aux buffer is not setup until 96562306a36Sopenharmony_ci * a subsequent mmap, or creates the profiling event in a 96662306a36Sopenharmony_ci * disabled state and explicitly PERF_EVENT_IOC_ENABLEs it 96762306a36Sopenharmony_ci * once the buffer has been created. 96862306a36Sopenharmony_ci */ 96962306a36Sopenharmony_ci .task_ctx_nr = perf_sw_context, 97062306a36Sopenharmony_ci .event_init = arm_spe_pmu_event_init, 97162306a36Sopenharmony_ci .add = arm_spe_pmu_add, 97262306a36Sopenharmony_ci .del = arm_spe_pmu_del, 97362306a36Sopenharmony_ci .start = arm_spe_pmu_start, 97462306a36Sopenharmony_ci .stop = arm_spe_pmu_stop, 97562306a36Sopenharmony_ci .read = arm_spe_pmu_read, 97662306a36Sopenharmony_ci .setup_aux = arm_spe_pmu_setup_aux, 97762306a36Sopenharmony_ci .free_aux = arm_spe_pmu_free_aux, 97862306a36Sopenharmony_ci }; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci idx = atomic_inc_return(&pmu_idx); 98162306a36Sopenharmony_ci name = devm_kasprintf(dev, GFP_KERNEL, "%s_%d", PMUNAME, idx); 98262306a36Sopenharmony_ci if (!name) { 98362306a36Sopenharmony_ci dev_err(dev, "failed to allocate name for pmu %d\n", idx); 98462306a36Sopenharmony_ci return -ENOMEM; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return perf_pmu_register(&spe_pmu->pmu, name, -1); 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic void arm_spe_pmu_perf_destroy(struct arm_spe_pmu *spe_pmu) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci perf_pmu_unregister(&spe_pmu->pmu); 99362306a36Sopenharmony_ci} 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_cistatic void __arm_spe_pmu_dev_probe(void *info) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci int fld; 99862306a36Sopenharmony_ci u64 reg; 99962306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = info; 100062306a36Sopenharmony_ci struct device *dev = &spe_pmu->pdev->dev; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci fld = cpuid_feature_extract_unsigned_field(read_cpuid(ID_AA64DFR0_EL1), 100362306a36Sopenharmony_ci ID_AA64DFR0_EL1_PMSVer_SHIFT); 100462306a36Sopenharmony_ci if (!fld) { 100562306a36Sopenharmony_ci dev_err(dev, 100662306a36Sopenharmony_ci "unsupported ID_AA64DFR0_EL1.PMSVer [%d] on CPU %d\n", 100762306a36Sopenharmony_ci fld, smp_processor_id()); 100862306a36Sopenharmony_ci return; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci spe_pmu->pmsver = (u16)fld; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci /* Read PMBIDR first to determine whether or not we have access */ 101362306a36Sopenharmony_ci reg = read_sysreg_s(SYS_PMBIDR_EL1); 101462306a36Sopenharmony_ci if (FIELD_GET(PMBIDR_EL1_P, reg)) { 101562306a36Sopenharmony_ci dev_err(dev, 101662306a36Sopenharmony_ci "profiling buffer owned by higher exception level\n"); 101762306a36Sopenharmony_ci return; 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* Minimum alignment. If it's out-of-range, then fail the probe */ 102162306a36Sopenharmony_ci fld = FIELD_GET(PMBIDR_EL1_ALIGN, reg); 102262306a36Sopenharmony_ci spe_pmu->align = 1 << fld; 102362306a36Sopenharmony_ci if (spe_pmu->align > SZ_2K) { 102462306a36Sopenharmony_ci dev_err(dev, "unsupported PMBIDR.Align [%d] on CPU %d\n", 102562306a36Sopenharmony_ci fld, smp_processor_id()); 102662306a36Sopenharmony_ci return; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci /* It's now safe to read PMSIDR and figure out what we've got */ 103062306a36Sopenharmony_ci reg = read_sysreg_s(SYS_PMSIDR_EL1); 103162306a36Sopenharmony_ci if (FIELD_GET(PMSIDR_EL1_FE, reg)) 103262306a36Sopenharmony_ci spe_pmu->features |= SPE_PMU_FEAT_FILT_EVT; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (FIELD_GET(PMSIDR_EL1_FnE, reg)) 103562306a36Sopenharmony_ci spe_pmu->features |= SPE_PMU_FEAT_INV_FILT_EVT; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (FIELD_GET(PMSIDR_EL1_FT, reg)) 103862306a36Sopenharmony_ci spe_pmu->features |= SPE_PMU_FEAT_FILT_TYP; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (FIELD_GET(PMSIDR_EL1_FL, reg)) 104162306a36Sopenharmony_ci spe_pmu->features |= SPE_PMU_FEAT_FILT_LAT; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if (FIELD_GET(PMSIDR_EL1_ARCHINST, reg)) 104462306a36Sopenharmony_ci spe_pmu->features |= SPE_PMU_FEAT_ARCH_INST; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (FIELD_GET(PMSIDR_EL1_LDS, reg)) 104762306a36Sopenharmony_ci spe_pmu->features |= SPE_PMU_FEAT_LDS; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (FIELD_GET(PMSIDR_EL1_ERND, reg)) 105062306a36Sopenharmony_ci spe_pmu->features |= SPE_PMU_FEAT_ERND; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci /* This field has a spaced out encoding, so just use a look-up */ 105362306a36Sopenharmony_ci fld = FIELD_GET(PMSIDR_EL1_INTERVAL, reg); 105462306a36Sopenharmony_ci switch (fld) { 105562306a36Sopenharmony_ci case PMSIDR_EL1_INTERVAL_256: 105662306a36Sopenharmony_ci spe_pmu->min_period = 256; 105762306a36Sopenharmony_ci break; 105862306a36Sopenharmony_ci case PMSIDR_EL1_INTERVAL_512: 105962306a36Sopenharmony_ci spe_pmu->min_period = 512; 106062306a36Sopenharmony_ci break; 106162306a36Sopenharmony_ci case PMSIDR_EL1_INTERVAL_768: 106262306a36Sopenharmony_ci spe_pmu->min_period = 768; 106362306a36Sopenharmony_ci break; 106462306a36Sopenharmony_ci case PMSIDR_EL1_INTERVAL_1024: 106562306a36Sopenharmony_ci spe_pmu->min_period = 1024; 106662306a36Sopenharmony_ci break; 106762306a36Sopenharmony_ci case PMSIDR_EL1_INTERVAL_1536: 106862306a36Sopenharmony_ci spe_pmu->min_period = 1536; 106962306a36Sopenharmony_ci break; 107062306a36Sopenharmony_ci case PMSIDR_EL1_INTERVAL_2048: 107162306a36Sopenharmony_ci spe_pmu->min_period = 2048; 107262306a36Sopenharmony_ci break; 107362306a36Sopenharmony_ci case PMSIDR_EL1_INTERVAL_3072: 107462306a36Sopenharmony_ci spe_pmu->min_period = 3072; 107562306a36Sopenharmony_ci break; 107662306a36Sopenharmony_ci default: 107762306a36Sopenharmony_ci dev_warn(dev, "unknown PMSIDR_EL1.Interval [%d]; assuming 8\n", 107862306a36Sopenharmony_ci fld); 107962306a36Sopenharmony_ci fallthrough; 108062306a36Sopenharmony_ci case PMSIDR_EL1_INTERVAL_4096: 108162306a36Sopenharmony_ci spe_pmu->min_period = 4096; 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci /* Maximum record size. If it's out-of-range, then fail the probe */ 108562306a36Sopenharmony_ci fld = FIELD_GET(PMSIDR_EL1_MAXSIZE, reg); 108662306a36Sopenharmony_ci spe_pmu->max_record_sz = 1 << fld; 108762306a36Sopenharmony_ci if (spe_pmu->max_record_sz > SZ_2K || spe_pmu->max_record_sz < 16) { 108862306a36Sopenharmony_ci dev_err(dev, "unsupported PMSIDR_EL1.MaxSize [%d] on CPU %d\n", 108962306a36Sopenharmony_ci fld, smp_processor_id()); 109062306a36Sopenharmony_ci return; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci fld = FIELD_GET(PMSIDR_EL1_COUNTSIZE, reg); 109462306a36Sopenharmony_ci switch (fld) { 109562306a36Sopenharmony_ci default: 109662306a36Sopenharmony_ci dev_warn(dev, "unknown PMSIDR_EL1.CountSize [%d]; assuming 2\n", 109762306a36Sopenharmony_ci fld); 109862306a36Sopenharmony_ci fallthrough; 109962306a36Sopenharmony_ci case PMSIDR_EL1_COUNTSIZE_12_BIT_SAT: 110062306a36Sopenharmony_ci spe_pmu->counter_sz = 12; 110162306a36Sopenharmony_ci break; 110262306a36Sopenharmony_ci case PMSIDR_EL1_COUNTSIZE_16_BIT_SAT: 110362306a36Sopenharmony_ci spe_pmu->counter_sz = 16; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci dev_info(dev, 110762306a36Sopenharmony_ci "probed SPEv1.%d for CPUs %*pbl [max_record_sz %u, align %u, features 0x%llx]\n", 110862306a36Sopenharmony_ci spe_pmu->pmsver - 1, cpumask_pr_args(&spe_pmu->supported_cpus), 110962306a36Sopenharmony_ci spe_pmu->max_record_sz, spe_pmu->align, spe_pmu->features); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci spe_pmu->features |= SPE_PMU_FEAT_DEV_PROBED; 111262306a36Sopenharmony_ci} 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic void __arm_spe_pmu_reset_local(void) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci /* 111762306a36Sopenharmony_ci * This is probably overkill, as we have no idea where we're 111862306a36Sopenharmony_ci * draining any buffered data to... 111962306a36Sopenharmony_ci */ 112062306a36Sopenharmony_ci arm_spe_pmu_disable_and_drain_local(); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci /* Reset the buffer base pointer */ 112362306a36Sopenharmony_ci write_sysreg_s(0, SYS_PMBPTR_EL1); 112462306a36Sopenharmony_ci isb(); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci /* Clear any pending management interrupts */ 112762306a36Sopenharmony_ci write_sysreg_s(0, SYS_PMBSR_EL1); 112862306a36Sopenharmony_ci isb(); 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cistatic void __arm_spe_pmu_setup_one(void *info) 113262306a36Sopenharmony_ci{ 113362306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = info; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci __arm_spe_pmu_reset_local(); 113662306a36Sopenharmony_ci enable_percpu_irq(spe_pmu->irq, IRQ_TYPE_NONE); 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic void __arm_spe_pmu_stop_one(void *info) 114062306a36Sopenharmony_ci{ 114162306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = info; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci disable_percpu_irq(spe_pmu->irq); 114462306a36Sopenharmony_ci __arm_spe_pmu_reset_local(); 114562306a36Sopenharmony_ci} 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_cistatic int arm_spe_pmu_cpu_startup(unsigned int cpu, struct hlist_node *node) 114862306a36Sopenharmony_ci{ 114962306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci spe_pmu = hlist_entry_safe(node, struct arm_spe_pmu, hotplug_node); 115262306a36Sopenharmony_ci if (!cpumask_test_cpu(cpu, &spe_pmu->supported_cpus)) 115362306a36Sopenharmony_ci return 0; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci __arm_spe_pmu_setup_one(spe_pmu); 115662306a36Sopenharmony_ci return 0; 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic int arm_spe_pmu_cpu_teardown(unsigned int cpu, struct hlist_node *node) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci spe_pmu = hlist_entry_safe(node, struct arm_spe_pmu, hotplug_node); 116462306a36Sopenharmony_ci if (!cpumask_test_cpu(cpu, &spe_pmu->supported_cpus)) 116562306a36Sopenharmony_ci return 0; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci __arm_spe_pmu_stop_one(spe_pmu); 116862306a36Sopenharmony_ci return 0; 116962306a36Sopenharmony_ci} 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_cistatic int arm_spe_pmu_dev_init(struct arm_spe_pmu *spe_pmu) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci int ret; 117462306a36Sopenharmony_ci cpumask_t *mask = &spe_pmu->supported_cpus; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci /* Make sure we probe the hardware on a relevant CPU */ 117762306a36Sopenharmony_ci ret = smp_call_function_any(mask, __arm_spe_pmu_dev_probe, spe_pmu, 1); 117862306a36Sopenharmony_ci if (ret || !(spe_pmu->features & SPE_PMU_FEAT_DEV_PROBED)) 117962306a36Sopenharmony_ci return -ENXIO; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* Request our PPIs (note that the IRQ is still disabled) */ 118262306a36Sopenharmony_ci ret = request_percpu_irq(spe_pmu->irq, arm_spe_pmu_irq_handler, DRVNAME, 118362306a36Sopenharmony_ci spe_pmu->handle); 118462306a36Sopenharmony_ci if (ret) 118562306a36Sopenharmony_ci return ret; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci /* 118862306a36Sopenharmony_ci * Register our hotplug notifier now so we don't miss any events. 118962306a36Sopenharmony_ci * This will enable the IRQ for any supported CPUs that are already 119062306a36Sopenharmony_ci * up. 119162306a36Sopenharmony_ci */ 119262306a36Sopenharmony_ci ret = cpuhp_state_add_instance(arm_spe_pmu_online, 119362306a36Sopenharmony_ci &spe_pmu->hotplug_node); 119462306a36Sopenharmony_ci if (ret) 119562306a36Sopenharmony_ci free_percpu_irq(spe_pmu->irq, spe_pmu->handle); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci return ret; 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic void arm_spe_pmu_dev_teardown(struct arm_spe_pmu *spe_pmu) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci cpuhp_state_remove_instance(arm_spe_pmu_online, &spe_pmu->hotplug_node); 120362306a36Sopenharmony_ci free_percpu_irq(spe_pmu->irq, spe_pmu->handle); 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci/* Driver and device probing */ 120762306a36Sopenharmony_cistatic int arm_spe_pmu_irq_probe(struct arm_spe_pmu *spe_pmu) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci struct platform_device *pdev = spe_pmu->pdev; 121062306a36Sopenharmony_ci int irq = platform_get_irq(pdev, 0); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci if (irq < 0) 121362306a36Sopenharmony_ci return -ENXIO; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (!irq_is_percpu(irq)) { 121662306a36Sopenharmony_ci dev_err(&pdev->dev, "expected PPI but got SPI (%d)\n", irq); 121762306a36Sopenharmony_ci return -EINVAL; 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (irq_get_percpu_devid_partition(irq, &spe_pmu->supported_cpus)) { 122162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get PPI partition (%d)\n", irq); 122262306a36Sopenharmony_ci return -EINVAL; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci spe_pmu->irq = irq; 122662306a36Sopenharmony_ci return 0; 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cistatic const struct of_device_id arm_spe_pmu_of_match[] = { 123062306a36Sopenharmony_ci { .compatible = "arm,statistical-profiling-extension-v1", .data = (void *)1 }, 123162306a36Sopenharmony_ci { /* Sentinel */ }, 123262306a36Sopenharmony_ci}; 123362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, arm_spe_pmu_of_match); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic const struct platform_device_id arm_spe_match[] = { 123662306a36Sopenharmony_ci { ARMV8_SPE_PDEV_NAME, 0}, 123762306a36Sopenharmony_ci { } 123862306a36Sopenharmony_ci}; 123962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, arm_spe_match); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_cistatic int arm_spe_pmu_device_probe(struct platform_device *pdev) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci int ret; 124462306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu; 124562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci /* 124862306a36Sopenharmony_ci * If kernelspace is unmapped when running at EL0, then the SPE 124962306a36Sopenharmony_ci * buffer will fault and prematurely terminate the AUX session. 125062306a36Sopenharmony_ci */ 125162306a36Sopenharmony_ci if (arm64_kernel_unmapped_at_el0()) { 125262306a36Sopenharmony_ci dev_warn_once(dev, "profiling buffer inaccessible. Try passing \"kpti=off\" on the kernel command line\n"); 125362306a36Sopenharmony_ci return -EPERM; 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci spe_pmu = devm_kzalloc(dev, sizeof(*spe_pmu), GFP_KERNEL); 125762306a36Sopenharmony_ci if (!spe_pmu) 125862306a36Sopenharmony_ci return -ENOMEM; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci spe_pmu->handle = alloc_percpu(typeof(*spe_pmu->handle)); 126162306a36Sopenharmony_ci if (!spe_pmu->handle) 126262306a36Sopenharmony_ci return -ENOMEM; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci spe_pmu->pdev = pdev; 126562306a36Sopenharmony_ci platform_set_drvdata(pdev, spe_pmu); 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci ret = arm_spe_pmu_irq_probe(spe_pmu); 126862306a36Sopenharmony_ci if (ret) 126962306a36Sopenharmony_ci goto out_free_handle; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci ret = arm_spe_pmu_dev_init(spe_pmu); 127262306a36Sopenharmony_ci if (ret) 127362306a36Sopenharmony_ci goto out_free_handle; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci ret = arm_spe_pmu_perf_init(spe_pmu); 127662306a36Sopenharmony_ci if (ret) 127762306a36Sopenharmony_ci goto out_teardown_dev; 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci return 0; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ciout_teardown_dev: 128262306a36Sopenharmony_ci arm_spe_pmu_dev_teardown(spe_pmu); 128362306a36Sopenharmony_ciout_free_handle: 128462306a36Sopenharmony_ci free_percpu(spe_pmu->handle); 128562306a36Sopenharmony_ci return ret; 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_cistatic int arm_spe_pmu_device_remove(struct platform_device *pdev) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci struct arm_spe_pmu *spe_pmu = platform_get_drvdata(pdev); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci arm_spe_pmu_perf_destroy(spe_pmu); 129362306a36Sopenharmony_ci arm_spe_pmu_dev_teardown(spe_pmu); 129462306a36Sopenharmony_ci free_percpu(spe_pmu->handle); 129562306a36Sopenharmony_ci return 0; 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic struct platform_driver arm_spe_pmu_driver = { 129962306a36Sopenharmony_ci .id_table = arm_spe_match, 130062306a36Sopenharmony_ci .driver = { 130162306a36Sopenharmony_ci .name = DRVNAME, 130262306a36Sopenharmony_ci .of_match_table = of_match_ptr(arm_spe_pmu_of_match), 130362306a36Sopenharmony_ci .suppress_bind_attrs = true, 130462306a36Sopenharmony_ci }, 130562306a36Sopenharmony_ci .probe = arm_spe_pmu_device_probe, 130662306a36Sopenharmony_ci .remove = arm_spe_pmu_device_remove, 130762306a36Sopenharmony_ci}; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cistatic int __init arm_spe_pmu_init(void) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci int ret; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, DRVNAME, 131462306a36Sopenharmony_ci arm_spe_pmu_cpu_startup, 131562306a36Sopenharmony_ci arm_spe_pmu_cpu_teardown); 131662306a36Sopenharmony_ci if (ret < 0) 131762306a36Sopenharmony_ci return ret; 131862306a36Sopenharmony_ci arm_spe_pmu_online = ret; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci ret = platform_driver_register(&arm_spe_pmu_driver); 132162306a36Sopenharmony_ci if (ret) 132262306a36Sopenharmony_ci cpuhp_remove_multi_state(arm_spe_pmu_online); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci return ret; 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_cistatic void __exit arm_spe_pmu_exit(void) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci platform_driver_unregister(&arm_spe_pmu_driver); 133062306a36Sopenharmony_ci cpuhp_remove_multi_state(arm_spe_pmu_online); 133162306a36Sopenharmony_ci} 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_cimodule_init(arm_spe_pmu_init); 133462306a36Sopenharmony_cimodule_exit(arm_spe_pmu_exit); 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ciMODULE_DESCRIPTION("Perf driver for the ARMv8.2 Statistical Profiling Extension"); 133762306a36Sopenharmony_ciMODULE_AUTHOR("Will Deacon <will.deacon@arm.com>"); 133862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1339