162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ARM DynamIQ Shared Unit (DSU) PMU driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) ARM Limited, 2017. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on ARM CCI-PMU, ARMv8 PMU-v3 drivers. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define PMUNAME "arm_dsu" 1162306a36Sopenharmony_ci#define DRVNAME PMUNAME "_pmu" 1262306a36Sopenharmony_ci#define pr_fmt(fmt) DRVNAME ": " fmt 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/acpi.h> 1562306a36Sopenharmony_ci#include <linux/bitmap.h> 1662306a36Sopenharmony_ci#include <linux/bitops.h> 1762306a36Sopenharmony_ci#include <linux/bug.h> 1862306a36Sopenharmony_ci#include <linux/cpumask.h> 1962306a36Sopenharmony_ci#include <linux/device.h> 2062306a36Sopenharmony_ci#include <linux/interrupt.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <linux/perf_event.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/spinlock.h> 2762306a36Sopenharmony_ci#include <linux/smp.h> 2862306a36Sopenharmony_ci#include <linux/sysfs.h> 2962306a36Sopenharmony_ci#include <linux/types.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <asm/arm_dsu_pmu.h> 3262306a36Sopenharmony_ci#include <asm/local64.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* PMU event codes */ 3562306a36Sopenharmony_ci#define DSU_PMU_EVT_CYCLES 0x11 3662306a36Sopenharmony_ci#define DSU_PMU_EVT_CHAIN 0x1e 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define DSU_PMU_MAX_COMMON_EVENTS 0x40 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define DSU_PMU_MAX_HW_CNTRS 32 4162306a36Sopenharmony_ci#define DSU_PMU_HW_COUNTER_MASK (DSU_PMU_MAX_HW_CNTRS - 1) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define CLUSTERPMCR_E BIT(0) 4462306a36Sopenharmony_ci#define CLUSTERPMCR_P BIT(1) 4562306a36Sopenharmony_ci#define CLUSTERPMCR_C BIT(2) 4662306a36Sopenharmony_ci#define CLUSTERPMCR_N_SHIFT 11 4762306a36Sopenharmony_ci#define CLUSTERPMCR_N_MASK 0x1f 4862306a36Sopenharmony_ci#define CLUSTERPMCR_IDCODE_SHIFT 16 4962306a36Sopenharmony_ci#define CLUSTERPMCR_IDCODE_MASK 0xff 5062306a36Sopenharmony_ci#define CLUSTERPMCR_IMP_SHIFT 24 5162306a36Sopenharmony_ci#define CLUSTERPMCR_IMP_MASK 0xff 5262306a36Sopenharmony_ci#define CLUSTERPMCR_RES_MASK 0x7e8 5362306a36Sopenharmony_ci#define CLUSTERPMCR_RES_VAL 0x40 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define DSU_ACTIVE_CPU_MASK 0x0 5662306a36Sopenharmony_ci#define DSU_ASSOCIATED_CPU_MASK 0x1 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * We use the index of the counters as they appear in the counter 6062306a36Sopenharmony_ci * bit maps in the PMU registers (e.g CLUSTERPMSELR). 6162306a36Sopenharmony_ci * i.e, 6262306a36Sopenharmony_ci * counter 0 - Bit 0 6362306a36Sopenharmony_ci * counter 1 - Bit 1 6462306a36Sopenharmony_ci * ... 6562306a36Sopenharmony_ci * Cycle counter - Bit 31 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci#define DSU_PMU_IDX_CYCLE_COUNTER 31 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* All event counters are 32bit, with a 64bit Cycle counter */ 7062306a36Sopenharmony_ci#define DSU_PMU_COUNTER_WIDTH(idx) \ 7162306a36Sopenharmony_ci (((idx) == DSU_PMU_IDX_CYCLE_COUNTER) ? 64 : 32) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define DSU_PMU_COUNTER_MASK(idx) \ 7462306a36Sopenharmony_ci GENMASK_ULL((DSU_PMU_COUNTER_WIDTH((idx)) - 1), 0) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define DSU_EXT_ATTR(_name, _func, _config) \ 7762306a36Sopenharmony_ci (&((struct dev_ext_attribute[]) { \ 7862306a36Sopenharmony_ci { \ 7962306a36Sopenharmony_ci .attr = __ATTR(_name, 0444, _func, NULL), \ 8062306a36Sopenharmony_ci .var = (void *)_config \ 8162306a36Sopenharmony_ci } \ 8262306a36Sopenharmony_ci })[0].attr.attr) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define DSU_EVENT_ATTR(_name, _config) \ 8562306a36Sopenharmony_ci DSU_EXT_ATTR(_name, dsu_pmu_sysfs_event_show, (unsigned long)_config) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define DSU_FORMAT_ATTR(_name, _config) \ 8862306a36Sopenharmony_ci DSU_EXT_ATTR(_name, dsu_pmu_sysfs_format_show, (char *)_config) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define DSU_CPUMASK_ATTR(_name, _config) \ 9162306a36Sopenharmony_ci DSU_EXT_ATTR(_name, dsu_pmu_cpumask_show, (unsigned long)_config) 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistruct dsu_hw_events { 9462306a36Sopenharmony_ci DECLARE_BITMAP(used_mask, DSU_PMU_MAX_HW_CNTRS); 9562306a36Sopenharmony_ci struct perf_event *events[DSU_PMU_MAX_HW_CNTRS]; 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * struct dsu_pmu - DSU PMU descriptor 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * @pmu_lock : Protects accesses to DSU PMU register from normal vs 10262306a36Sopenharmony_ci * interrupt handler contexts. 10362306a36Sopenharmony_ci * @hw_events : Holds the event counter state. 10462306a36Sopenharmony_ci * @associated_cpus : CPUs attached to the DSU. 10562306a36Sopenharmony_ci * @active_cpu : CPU to which the PMU is bound for accesses. 10662306a36Sopenharmony_ci * @cpuhp_node : Node for CPU hotplug notifier link. 10762306a36Sopenharmony_ci * @num_counters : Number of event counters implemented by the PMU, 10862306a36Sopenharmony_ci * excluding the cycle counter. 10962306a36Sopenharmony_ci * @irq : Interrupt line for counter overflow. 11062306a36Sopenharmony_ci * @cpmceid_bitmap : Bitmap for the availability of architected common 11162306a36Sopenharmony_ci * events (event_code < 0x40). 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_cistruct dsu_pmu { 11462306a36Sopenharmony_ci struct pmu pmu; 11562306a36Sopenharmony_ci struct device *dev; 11662306a36Sopenharmony_ci raw_spinlock_t pmu_lock; 11762306a36Sopenharmony_ci struct dsu_hw_events hw_events; 11862306a36Sopenharmony_ci cpumask_t associated_cpus; 11962306a36Sopenharmony_ci cpumask_t active_cpu; 12062306a36Sopenharmony_ci struct hlist_node cpuhp_node; 12162306a36Sopenharmony_ci s8 num_counters; 12262306a36Sopenharmony_ci int irq; 12362306a36Sopenharmony_ci DECLARE_BITMAP(cpmceid_bitmap, DSU_PMU_MAX_COMMON_EVENTS); 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic unsigned long dsu_pmu_cpuhp_state; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic inline struct dsu_pmu *to_dsu_pmu(struct pmu *pmu) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci return container_of(pmu, struct dsu_pmu, pmu); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic ssize_t dsu_pmu_sysfs_event_show(struct device *dev, 13462306a36Sopenharmony_ci struct device_attribute *attr, 13562306a36Sopenharmony_ci char *buf) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct dev_ext_attribute *eattr = container_of(attr, 13862306a36Sopenharmony_ci struct dev_ext_attribute, attr); 13962306a36Sopenharmony_ci return sysfs_emit(buf, "event=0x%lx\n", (unsigned long)eattr->var); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic ssize_t dsu_pmu_sysfs_format_show(struct device *dev, 14362306a36Sopenharmony_ci struct device_attribute *attr, 14462306a36Sopenharmony_ci char *buf) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct dev_ext_attribute *eattr = container_of(attr, 14762306a36Sopenharmony_ci struct dev_ext_attribute, attr); 14862306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", (char *)eattr->var); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic ssize_t dsu_pmu_cpumask_show(struct device *dev, 15262306a36Sopenharmony_ci struct device_attribute *attr, 15362306a36Sopenharmony_ci char *buf) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct pmu *pmu = dev_get_drvdata(dev); 15662306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = to_dsu_pmu(pmu); 15762306a36Sopenharmony_ci struct dev_ext_attribute *eattr = container_of(attr, 15862306a36Sopenharmony_ci struct dev_ext_attribute, attr); 15962306a36Sopenharmony_ci unsigned long mask_id = (unsigned long)eattr->var; 16062306a36Sopenharmony_ci const cpumask_t *cpumask; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci switch (mask_id) { 16362306a36Sopenharmony_ci case DSU_ACTIVE_CPU_MASK: 16462306a36Sopenharmony_ci cpumask = &dsu_pmu->active_cpu; 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci case DSU_ASSOCIATED_CPU_MASK: 16762306a36Sopenharmony_ci cpumask = &dsu_pmu->associated_cpus; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci default: 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, cpumask); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic struct attribute *dsu_pmu_format_attrs[] = { 17662306a36Sopenharmony_ci DSU_FORMAT_ATTR(event, "config:0-31"), 17762306a36Sopenharmony_ci NULL, 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic const struct attribute_group dsu_pmu_format_attr_group = { 18162306a36Sopenharmony_ci .name = "format", 18262306a36Sopenharmony_ci .attrs = dsu_pmu_format_attrs, 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic struct attribute *dsu_pmu_event_attrs[] = { 18662306a36Sopenharmony_ci DSU_EVENT_ATTR(cycles, 0x11), 18762306a36Sopenharmony_ci DSU_EVENT_ATTR(bus_access, 0x19), 18862306a36Sopenharmony_ci DSU_EVENT_ATTR(memory_error, 0x1a), 18962306a36Sopenharmony_ci DSU_EVENT_ATTR(bus_cycles, 0x1d), 19062306a36Sopenharmony_ci DSU_EVENT_ATTR(l3d_cache_allocate, 0x29), 19162306a36Sopenharmony_ci DSU_EVENT_ATTR(l3d_cache_refill, 0x2a), 19262306a36Sopenharmony_ci DSU_EVENT_ATTR(l3d_cache, 0x2b), 19362306a36Sopenharmony_ci DSU_EVENT_ATTR(l3d_cache_wb, 0x2c), 19462306a36Sopenharmony_ci NULL, 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic umode_t 19862306a36Sopenharmony_cidsu_pmu_event_attr_is_visible(struct kobject *kobj, struct attribute *attr, 19962306a36Sopenharmony_ci int unused) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct pmu *pmu = dev_get_drvdata(kobj_to_dev(kobj)); 20262306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = to_dsu_pmu(pmu); 20362306a36Sopenharmony_ci struct dev_ext_attribute *eattr = container_of(attr, 20462306a36Sopenharmony_ci struct dev_ext_attribute, attr.attr); 20562306a36Sopenharmony_ci unsigned long evt = (unsigned long)eattr->var; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return test_bit(evt, dsu_pmu->cpmceid_bitmap) ? attr->mode : 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic const struct attribute_group dsu_pmu_events_attr_group = { 21162306a36Sopenharmony_ci .name = "events", 21262306a36Sopenharmony_ci .attrs = dsu_pmu_event_attrs, 21362306a36Sopenharmony_ci .is_visible = dsu_pmu_event_attr_is_visible, 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic struct attribute *dsu_pmu_cpumask_attrs[] = { 21762306a36Sopenharmony_ci DSU_CPUMASK_ATTR(cpumask, DSU_ACTIVE_CPU_MASK), 21862306a36Sopenharmony_ci DSU_CPUMASK_ATTR(associated_cpus, DSU_ASSOCIATED_CPU_MASK), 21962306a36Sopenharmony_ci NULL, 22062306a36Sopenharmony_ci}; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic const struct attribute_group dsu_pmu_cpumask_attr_group = { 22362306a36Sopenharmony_ci .attrs = dsu_pmu_cpumask_attrs, 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic const struct attribute_group *dsu_pmu_attr_groups[] = { 22762306a36Sopenharmony_ci &dsu_pmu_cpumask_attr_group, 22862306a36Sopenharmony_ci &dsu_pmu_events_attr_group, 22962306a36Sopenharmony_ci &dsu_pmu_format_attr_group, 23062306a36Sopenharmony_ci NULL, 23162306a36Sopenharmony_ci}; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int dsu_pmu_get_online_cpu_any_but(struct dsu_pmu *dsu_pmu, int cpu) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct cpumask online_supported; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci cpumask_and(&online_supported, 23862306a36Sopenharmony_ci &dsu_pmu->associated_cpus, cpu_online_mask); 23962306a36Sopenharmony_ci return cpumask_any_but(&online_supported, cpu); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic inline bool dsu_pmu_counter_valid(struct dsu_pmu *dsu_pmu, u32 idx) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci return (idx < dsu_pmu->num_counters) || 24562306a36Sopenharmony_ci (idx == DSU_PMU_IDX_CYCLE_COUNTER); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic inline u64 dsu_pmu_read_counter(struct perf_event *event) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci u64 val; 25162306a36Sopenharmony_ci unsigned long flags; 25262306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = to_dsu_pmu(event->pmu); 25362306a36Sopenharmony_ci int idx = event->hw.idx; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (WARN_ON(!cpumask_test_cpu(smp_processor_id(), 25662306a36Sopenharmony_ci &dsu_pmu->associated_cpus))) 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (!dsu_pmu_counter_valid(dsu_pmu, idx)) { 26062306a36Sopenharmony_ci dev_err(event->pmu->dev, 26162306a36Sopenharmony_ci "Trying reading invalid counter %d\n", idx); 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci raw_spin_lock_irqsave(&dsu_pmu->pmu_lock, flags); 26662306a36Sopenharmony_ci if (idx == DSU_PMU_IDX_CYCLE_COUNTER) 26762306a36Sopenharmony_ci val = __dsu_pmu_read_pmccntr(); 26862306a36Sopenharmony_ci else 26962306a36Sopenharmony_ci val = __dsu_pmu_read_counter(idx); 27062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&dsu_pmu->pmu_lock, flags); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return val; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void dsu_pmu_write_counter(struct perf_event *event, u64 val) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci unsigned long flags; 27862306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = to_dsu_pmu(event->pmu); 27962306a36Sopenharmony_ci int idx = event->hw.idx; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (WARN_ON(!cpumask_test_cpu(smp_processor_id(), 28262306a36Sopenharmony_ci &dsu_pmu->associated_cpus))) 28362306a36Sopenharmony_ci return; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (!dsu_pmu_counter_valid(dsu_pmu, idx)) { 28662306a36Sopenharmony_ci dev_err(event->pmu->dev, 28762306a36Sopenharmony_ci "writing to invalid counter %d\n", idx); 28862306a36Sopenharmony_ci return; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci raw_spin_lock_irqsave(&dsu_pmu->pmu_lock, flags); 29262306a36Sopenharmony_ci if (idx == DSU_PMU_IDX_CYCLE_COUNTER) 29362306a36Sopenharmony_ci __dsu_pmu_write_pmccntr(val); 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci __dsu_pmu_write_counter(idx, val); 29662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&dsu_pmu->pmu_lock, flags); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int dsu_pmu_get_event_idx(struct dsu_hw_events *hw_events, 30062306a36Sopenharmony_ci struct perf_event *event) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci int idx; 30362306a36Sopenharmony_ci unsigned long evtype = event->attr.config; 30462306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = to_dsu_pmu(event->pmu); 30562306a36Sopenharmony_ci unsigned long *used_mask = hw_events->used_mask; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (evtype == DSU_PMU_EVT_CYCLES) { 30862306a36Sopenharmony_ci if (test_and_set_bit(DSU_PMU_IDX_CYCLE_COUNTER, used_mask)) 30962306a36Sopenharmony_ci return -EAGAIN; 31062306a36Sopenharmony_ci return DSU_PMU_IDX_CYCLE_COUNTER; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci idx = find_first_zero_bit(used_mask, dsu_pmu->num_counters); 31462306a36Sopenharmony_ci if (idx >= dsu_pmu->num_counters) 31562306a36Sopenharmony_ci return -EAGAIN; 31662306a36Sopenharmony_ci set_bit(idx, hw_events->used_mask); 31762306a36Sopenharmony_ci return idx; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void dsu_pmu_enable_counter(struct dsu_pmu *dsu_pmu, int idx) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci __dsu_pmu_counter_interrupt_enable(idx); 32362306a36Sopenharmony_ci __dsu_pmu_enable_counter(idx); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void dsu_pmu_disable_counter(struct dsu_pmu *dsu_pmu, int idx) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci __dsu_pmu_disable_counter(idx); 32962306a36Sopenharmony_ci __dsu_pmu_counter_interrupt_disable(idx); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic inline void dsu_pmu_set_event(struct dsu_pmu *dsu_pmu, 33362306a36Sopenharmony_ci struct perf_event *event) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci int idx = event->hw.idx; 33662306a36Sopenharmony_ci unsigned long flags; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!dsu_pmu_counter_valid(dsu_pmu, idx)) { 33962306a36Sopenharmony_ci dev_err(event->pmu->dev, 34062306a36Sopenharmony_ci "Trying to set invalid counter %d\n", idx); 34162306a36Sopenharmony_ci return; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci raw_spin_lock_irqsave(&dsu_pmu->pmu_lock, flags); 34562306a36Sopenharmony_ci __dsu_pmu_set_event(idx, event->hw.config_base); 34662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&dsu_pmu->pmu_lock, flags); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void dsu_pmu_event_update(struct perf_event *event) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 35262306a36Sopenharmony_ci u64 delta, prev_count, new_count; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci do { 35562306a36Sopenharmony_ci /* We may also be called from the irq handler */ 35662306a36Sopenharmony_ci prev_count = local64_read(&hwc->prev_count); 35762306a36Sopenharmony_ci new_count = dsu_pmu_read_counter(event); 35862306a36Sopenharmony_ci } while (local64_cmpxchg(&hwc->prev_count, prev_count, new_count) != 35962306a36Sopenharmony_ci prev_count); 36062306a36Sopenharmony_ci delta = (new_count - prev_count) & DSU_PMU_COUNTER_MASK(hwc->idx); 36162306a36Sopenharmony_ci local64_add(delta, &event->count); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic void dsu_pmu_read(struct perf_event *event) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci dsu_pmu_event_update(event); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic inline u32 dsu_pmu_get_reset_overflow(void) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci return __dsu_pmu_get_reset_overflow(); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/** 37562306a36Sopenharmony_ci * dsu_pmu_set_event_period: Set the period for the counter. 37662306a36Sopenharmony_ci * 37762306a36Sopenharmony_ci * All DSU PMU event counters, except the cycle counter are 32bit 37862306a36Sopenharmony_ci * counters. To handle cases of extreme interrupt latency, we program 37962306a36Sopenharmony_ci * the counter with half of the max count for the counters. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_cistatic void dsu_pmu_set_event_period(struct perf_event *event) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci int idx = event->hw.idx; 38462306a36Sopenharmony_ci u64 val = DSU_PMU_COUNTER_MASK(idx) >> 1; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci local64_set(&event->hw.prev_count, val); 38762306a36Sopenharmony_ci dsu_pmu_write_counter(event, val); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic irqreturn_t dsu_pmu_handle_irq(int irq_num, void *dev) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci int i; 39362306a36Sopenharmony_ci bool handled = false; 39462306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = dev; 39562306a36Sopenharmony_ci struct dsu_hw_events *hw_events = &dsu_pmu->hw_events; 39662306a36Sopenharmony_ci unsigned long overflow; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci overflow = dsu_pmu_get_reset_overflow(); 39962306a36Sopenharmony_ci if (!overflow) 40062306a36Sopenharmony_ci return IRQ_NONE; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci for_each_set_bit(i, &overflow, DSU_PMU_MAX_HW_CNTRS) { 40362306a36Sopenharmony_ci struct perf_event *event = hw_events->events[i]; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!event) 40662306a36Sopenharmony_ci continue; 40762306a36Sopenharmony_ci dsu_pmu_event_update(event); 40862306a36Sopenharmony_ci dsu_pmu_set_event_period(event); 40962306a36Sopenharmony_ci handled = true; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return IRQ_RETVAL(handled); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void dsu_pmu_start(struct perf_event *event, int pmu_flags) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = to_dsu_pmu(event->pmu); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* We always reprogram the counter */ 42062306a36Sopenharmony_ci if (pmu_flags & PERF_EF_RELOAD) 42162306a36Sopenharmony_ci WARN_ON(!(event->hw.state & PERF_HES_UPTODATE)); 42262306a36Sopenharmony_ci dsu_pmu_set_event_period(event); 42362306a36Sopenharmony_ci if (event->hw.idx != DSU_PMU_IDX_CYCLE_COUNTER) 42462306a36Sopenharmony_ci dsu_pmu_set_event(dsu_pmu, event); 42562306a36Sopenharmony_ci event->hw.state = 0; 42662306a36Sopenharmony_ci dsu_pmu_enable_counter(dsu_pmu, event->hw.idx); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic void dsu_pmu_stop(struct perf_event *event, int pmu_flags) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = to_dsu_pmu(event->pmu); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (event->hw.state & PERF_HES_STOPPED) 43462306a36Sopenharmony_ci return; 43562306a36Sopenharmony_ci dsu_pmu_disable_counter(dsu_pmu, event->hw.idx); 43662306a36Sopenharmony_ci dsu_pmu_event_update(event); 43762306a36Sopenharmony_ci event->hw.state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic int dsu_pmu_add(struct perf_event *event, int flags) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = to_dsu_pmu(event->pmu); 44362306a36Sopenharmony_ci struct dsu_hw_events *hw_events = &dsu_pmu->hw_events; 44462306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 44562306a36Sopenharmony_ci int idx; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), 44862306a36Sopenharmony_ci &dsu_pmu->associated_cpus))) 44962306a36Sopenharmony_ci return -ENOENT; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci idx = dsu_pmu_get_event_idx(hw_events, event); 45262306a36Sopenharmony_ci if (idx < 0) 45362306a36Sopenharmony_ci return idx; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci hwc->idx = idx; 45662306a36Sopenharmony_ci hw_events->events[idx] = event; 45762306a36Sopenharmony_ci hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (flags & PERF_EF_START) 46062306a36Sopenharmony_ci dsu_pmu_start(event, PERF_EF_RELOAD); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci perf_event_update_userpage(event); 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic void dsu_pmu_del(struct perf_event *event, int flags) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = to_dsu_pmu(event->pmu); 46962306a36Sopenharmony_ci struct dsu_hw_events *hw_events = &dsu_pmu->hw_events; 47062306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 47162306a36Sopenharmony_ci int idx = hwc->idx; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci dsu_pmu_stop(event, PERF_EF_UPDATE); 47462306a36Sopenharmony_ci hw_events->events[idx] = NULL; 47562306a36Sopenharmony_ci clear_bit(idx, hw_events->used_mask); 47662306a36Sopenharmony_ci perf_event_update_userpage(event); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic void dsu_pmu_enable(struct pmu *pmu) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci u32 pmcr; 48262306a36Sopenharmony_ci unsigned long flags; 48362306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = to_dsu_pmu(pmu); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* If no counters are added, skip enabling the PMU */ 48662306a36Sopenharmony_ci if (bitmap_empty(dsu_pmu->hw_events.used_mask, DSU_PMU_MAX_HW_CNTRS)) 48762306a36Sopenharmony_ci return; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci raw_spin_lock_irqsave(&dsu_pmu->pmu_lock, flags); 49062306a36Sopenharmony_ci pmcr = __dsu_pmu_read_pmcr(); 49162306a36Sopenharmony_ci pmcr |= CLUSTERPMCR_E; 49262306a36Sopenharmony_ci __dsu_pmu_write_pmcr(pmcr); 49362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&dsu_pmu->pmu_lock, flags); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic void dsu_pmu_disable(struct pmu *pmu) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci u32 pmcr; 49962306a36Sopenharmony_ci unsigned long flags; 50062306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = to_dsu_pmu(pmu); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci raw_spin_lock_irqsave(&dsu_pmu->pmu_lock, flags); 50362306a36Sopenharmony_ci pmcr = __dsu_pmu_read_pmcr(); 50462306a36Sopenharmony_ci pmcr &= ~CLUSTERPMCR_E; 50562306a36Sopenharmony_ci __dsu_pmu_write_pmcr(pmcr); 50662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&dsu_pmu->pmu_lock, flags); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic bool dsu_pmu_validate_event(struct pmu *pmu, 51062306a36Sopenharmony_ci struct dsu_hw_events *hw_events, 51162306a36Sopenharmony_ci struct perf_event *event) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci if (is_software_event(event)) 51462306a36Sopenharmony_ci return true; 51562306a36Sopenharmony_ci /* Reject groups spanning multiple HW PMUs. */ 51662306a36Sopenharmony_ci if (event->pmu != pmu) 51762306a36Sopenharmony_ci return false; 51862306a36Sopenharmony_ci return dsu_pmu_get_event_idx(hw_events, event) >= 0; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci/* 52262306a36Sopenharmony_ci * Make sure the group of events can be scheduled at once 52362306a36Sopenharmony_ci * on the PMU. 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_cistatic bool dsu_pmu_validate_group(struct perf_event *event) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct perf_event *sibling, *leader = event->group_leader; 52862306a36Sopenharmony_ci struct dsu_hw_events fake_hw; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (event->group_leader == event) 53162306a36Sopenharmony_ci return true; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci memset(fake_hw.used_mask, 0, sizeof(fake_hw.used_mask)); 53462306a36Sopenharmony_ci if (!dsu_pmu_validate_event(event->pmu, &fake_hw, leader)) 53562306a36Sopenharmony_ci return false; 53662306a36Sopenharmony_ci for_each_sibling_event(sibling, leader) { 53762306a36Sopenharmony_ci if (!dsu_pmu_validate_event(event->pmu, &fake_hw, sibling)) 53862306a36Sopenharmony_ci return false; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci return dsu_pmu_validate_event(event->pmu, &fake_hw, event); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic int dsu_pmu_event_init(struct perf_event *event) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = to_dsu_pmu(event->pmu); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (event->attr.type != event->pmu->type) 54862306a36Sopenharmony_ci return -ENOENT; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* We don't support sampling */ 55162306a36Sopenharmony_ci if (is_sampling_event(event)) { 55262306a36Sopenharmony_ci dev_dbg(dsu_pmu->pmu.dev, "Can't support sampling events\n"); 55362306a36Sopenharmony_ci return -EOPNOTSUPP; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* We cannot support task bound events */ 55762306a36Sopenharmony_ci if (event->cpu < 0 || event->attach_state & PERF_ATTACH_TASK) { 55862306a36Sopenharmony_ci dev_dbg(dsu_pmu->pmu.dev, "Can't support per-task counters\n"); 55962306a36Sopenharmony_ci return -EINVAL; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (has_branch_stack(event)) { 56362306a36Sopenharmony_ci dev_dbg(dsu_pmu->pmu.dev, "Can't support filtering\n"); 56462306a36Sopenharmony_ci return -EINVAL; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (!cpumask_test_cpu(event->cpu, &dsu_pmu->associated_cpus)) { 56862306a36Sopenharmony_ci dev_dbg(dsu_pmu->pmu.dev, 56962306a36Sopenharmony_ci "Requested cpu is not associated with the DSU\n"); 57062306a36Sopenharmony_ci return -EINVAL; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci /* 57362306a36Sopenharmony_ci * Choose the current active CPU to read the events. We don't want 57462306a36Sopenharmony_ci * to migrate the event contexts, irq handling etc to the requested 57562306a36Sopenharmony_ci * CPU. As long as the requested CPU is within the same DSU, we 57662306a36Sopenharmony_ci * are fine. 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_ci event->cpu = cpumask_first(&dsu_pmu->active_cpu); 57962306a36Sopenharmony_ci if (event->cpu >= nr_cpu_ids) 58062306a36Sopenharmony_ci return -EINVAL; 58162306a36Sopenharmony_ci if (!dsu_pmu_validate_group(event)) 58262306a36Sopenharmony_ci return -EINVAL; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci event->hw.config_base = event->attr.config; 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic struct dsu_pmu *dsu_pmu_alloc(struct platform_device *pdev) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci dsu_pmu = devm_kzalloc(&pdev->dev, sizeof(*dsu_pmu), GFP_KERNEL); 59362306a36Sopenharmony_ci if (!dsu_pmu) 59462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci raw_spin_lock_init(&dsu_pmu->pmu_lock); 59762306a36Sopenharmony_ci /* 59862306a36Sopenharmony_ci * Initialise the number of counters to -1, until we probe 59962306a36Sopenharmony_ci * the real number on a connected CPU. 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_ci dsu_pmu->num_counters = -1; 60262306a36Sopenharmony_ci return dsu_pmu; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/** 60662306a36Sopenharmony_ci * dsu_pmu_dt_get_cpus: Get the list of CPUs in the cluster 60762306a36Sopenharmony_ci * from device tree. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_cistatic int dsu_pmu_dt_get_cpus(struct device *dev, cpumask_t *mask) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci int i = 0, n, cpu; 61262306a36Sopenharmony_ci struct device_node *cpu_node; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci n = of_count_phandle_with_args(dev->of_node, "cpus", NULL); 61562306a36Sopenharmony_ci if (n <= 0) 61662306a36Sopenharmony_ci return -ENODEV; 61762306a36Sopenharmony_ci for (; i < n; i++) { 61862306a36Sopenharmony_ci cpu_node = of_parse_phandle(dev->of_node, "cpus", i); 61962306a36Sopenharmony_ci if (!cpu_node) 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci cpu = of_cpu_node_to_id(cpu_node); 62262306a36Sopenharmony_ci of_node_put(cpu_node); 62362306a36Sopenharmony_ci /* 62462306a36Sopenharmony_ci * We have to ignore the failures here and continue scanning 62562306a36Sopenharmony_ci * the list to handle cases where the nr_cpus could be capped 62662306a36Sopenharmony_ci * in the running kernel. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ci if (cpu < 0) 62962306a36Sopenharmony_ci continue; 63062306a36Sopenharmony_ci cpumask_set_cpu(cpu, mask); 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci/** 63662306a36Sopenharmony_ci * dsu_pmu_acpi_get_cpus: Get the list of CPUs in the cluster 63762306a36Sopenharmony_ci * from ACPI. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_cistatic int dsu_pmu_acpi_get_cpus(struct device *dev, cpumask_t *mask) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci#ifdef CONFIG_ACPI 64262306a36Sopenharmony_ci struct acpi_device *parent_adev = acpi_dev_parent(ACPI_COMPANION(dev)); 64362306a36Sopenharmony_ci int cpu; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* 64662306a36Sopenharmony_ci * A dsu pmu node is inside a cluster parent node along with cpu nodes. 64762306a36Sopenharmony_ci * We need to find out all cpus that have the same parent with this pmu. 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 65062306a36Sopenharmony_ci struct acpi_device *acpi_dev; 65162306a36Sopenharmony_ci struct device *cpu_dev = get_cpu_device(cpu); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (!cpu_dev) 65462306a36Sopenharmony_ci continue; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci acpi_dev = ACPI_COMPANION(cpu_dev); 65762306a36Sopenharmony_ci if (acpi_dev && acpi_dev_parent(acpi_dev) == parent_adev) 65862306a36Sopenharmony_ci cpumask_set_cpu(cpu, mask); 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci#endif 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci/* 66662306a36Sopenharmony_ci * dsu_pmu_probe_pmu: Probe the PMU details on a CPU in the cluster. 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_cistatic void dsu_pmu_probe_pmu(struct dsu_pmu *dsu_pmu) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci u64 num_counters; 67162306a36Sopenharmony_ci u32 cpmceid[2]; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci num_counters = (__dsu_pmu_read_pmcr() >> CLUSTERPMCR_N_SHIFT) & 67462306a36Sopenharmony_ci CLUSTERPMCR_N_MASK; 67562306a36Sopenharmony_ci /* We can only support up to 31 independent counters */ 67662306a36Sopenharmony_ci if (WARN_ON(num_counters > 31)) 67762306a36Sopenharmony_ci num_counters = 31; 67862306a36Sopenharmony_ci dsu_pmu->num_counters = num_counters; 67962306a36Sopenharmony_ci if (!dsu_pmu->num_counters) 68062306a36Sopenharmony_ci return; 68162306a36Sopenharmony_ci cpmceid[0] = __dsu_pmu_read_pmceid(0); 68262306a36Sopenharmony_ci cpmceid[1] = __dsu_pmu_read_pmceid(1); 68362306a36Sopenharmony_ci bitmap_from_arr32(dsu_pmu->cpmceid_bitmap, cpmceid, 68462306a36Sopenharmony_ci DSU_PMU_MAX_COMMON_EVENTS); 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic void dsu_pmu_set_active_cpu(int cpu, struct dsu_pmu *dsu_pmu) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci cpumask_set_cpu(cpu, &dsu_pmu->active_cpu); 69062306a36Sopenharmony_ci if (irq_set_affinity(dsu_pmu->irq, &dsu_pmu->active_cpu)) 69162306a36Sopenharmony_ci pr_warn("Failed to set irq affinity to %d\n", cpu); 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci/* 69562306a36Sopenharmony_ci * dsu_pmu_init_pmu: Initialise the DSU PMU configurations if 69662306a36Sopenharmony_ci * we haven't done it already. 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_cistatic void dsu_pmu_init_pmu(struct dsu_pmu *dsu_pmu) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci if (dsu_pmu->num_counters == -1) 70162306a36Sopenharmony_ci dsu_pmu_probe_pmu(dsu_pmu); 70262306a36Sopenharmony_ci /* Reset the interrupt overflow mask */ 70362306a36Sopenharmony_ci dsu_pmu_get_reset_overflow(); 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic int dsu_pmu_device_probe(struct platform_device *pdev) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci int irq, rc; 70962306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu; 71062306a36Sopenharmony_ci struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); 71162306a36Sopenharmony_ci char *name; 71262306a36Sopenharmony_ci static atomic_t pmu_idx = ATOMIC_INIT(-1); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci dsu_pmu = dsu_pmu_alloc(pdev); 71562306a36Sopenharmony_ci if (IS_ERR(dsu_pmu)) 71662306a36Sopenharmony_ci return PTR_ERR(dsu_pmu); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (is_of_node(fwnode)) 71962306a36Sopenharmony_ci rc = dsu_pmu_dt_get_cpus(&pdev->dev, &dsu_pmu->associated_cpus); 72062306a36Sopenharmony_ci else if (is_acpi_device_node(fwnode)) 72162306a36Sopenharmony_ci rc = dsu_pmu_acpi_get_cpus(&pdev->dev, &dsu_pmu->associated_cpus); 72262306a36Sopenharmony_ci else 72362306a36Sopenharmony_ci return -ENOENT; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (rc) { 72662306a36Sopenharmony_ci dev_warn(&pdev->dev, "Failed to parse the CPUs\n"); 72762306a36Sopenharmony_ci return rc; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 73162306a36Sopenharmony_ci if (irq < 0) 73262306a36Sopenharmony_ci return -EINVAL; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_%d", 73562306a36Sopenharmony_ci PMUNAME, atomic_inc_return(&pmu_idx)); 73662306a36Sopenharmony_ci if (!name) 73762306a36Sopenharmony_ci return -ENOMEM; 73862306a36Sopenharmony_ci rc = devm_request_irq(&pdev->dev, irq, dsu_pmu_handle_irq, 73962306a36Sopenharmony_ci IRQF_NOBALANCING, name, dsu_pmu); 74062306a36Sopenharmony_ci if (rc) { 74162306a36Sopenharmony_ci dev_warn(&pdev->dev, "Failed to request IRQ %d\n", irq); 74262306a36Sopenharmony_ci return rc; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci dsu_pmu->irq = irq; 74662306a36Sopenharmony_ci platform_set_drvdata(pdev, dsu_pmu); 74762306a36Sopenharmony_ci rc = cpuhp_state_add_instance(dsu_pmu_cpuhp_state, 74862306a36Sopenharmony_ci &dsu_pmu->cpuhp_node); 74962306a36Sopenharmony_ci if (rc) 75062306a36Sopenharmony_ci return rc; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci dsu_pmu->pmu = (struct pmu) { 75362306a36Sopenharmony_ci .task_ctx_nr = perf_invalid_context, 75462306a36Sopenharmony_ci .module = THIS_MODULE, 75562306a36Sopenharmony_ci .pmu_enable = dsu_pmu_enable, 75662306a36Sopenharmony_ci .pmu_disable = dsu_pmu_disable, 75762306a36Sopenharmony_ci .event_init = dsu_pmu_event_init, 75862306a36Sopenharmony_ci .add = dsu_pmu_add, 75962306a36Sopenharmony_ci .del = dsu_pmu_del, 76062306a36Sopenharmony_ci .start = dsu_pmu_start, 76162306a36Sopenharmony_ci .stop = dsu_pmu_stop, 76262306a36Sopenharmony_ci .read = dsu_pmu_read, 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci .attr_groups = dsu_pmu_attr_groups, 76562306a36Sopenharmony_ci .capabilities = PERF_PMU_CAP_NO_EXCLUDE, 76662306a36Sopenharmony_ci }; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci rc = perf_pmu_register(&dsu_pmu->pmu, name, -1); 76962306a36Sopenharmony_ci if (rc) { 77062306a36Sopenharmony_ci cpuhp_state_remove_instance(dsu_pmu_cpuhp_state, 77162306a36Sopenharmony_ci &dsu_pmu->cpuhp_node); 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci return rc; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic int dsu_pmu_device_remove(struct platform_device *pdev) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = platform_get_drvdata(pdev); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci perf_pmu_unregister(&dsu_pmu->pmu); 78262306a36Sopenharmony_ci cpuhp_state_remove_instance(dsu_pmu_cpuhp_state, &dsu_pmu->cpuhp_node); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci return 0; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic const struct of_device_id dsu_pmu_of_match[] = { 78862306a36Sopenharmony_ci { .compatible = "arm,dsu-pmu", }, 78962306a36Sopenharmony_ci {}, 79062306a36Sopenharmony_ci}; 79162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, dsu_pmu_of_match); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci#ifdef CONFIG_ACPI 79462306a36Sopenharmony_cistatic const struct acpi_device_id dsu_pmu_acpi_match[] = { 79562306a36Sopenharmony_ci { "ARMHD500", 0}, 79662306a36Sopenharmony_ci {}, 79762306a36Sopenharmony_ci}; 79862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, dsu_pmu_acpi_match); 79962306a36Sopenharmony_ci#endif 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic struct platform_driver dsu_pmu_driver = { 80262306a36Sopenharmony_ci .driver = { 80362306a36Sopenharmony_ci .name = DRVNAME, 80462306a36Sopenharmony_ci .of_match_table = of_match_ptr(dsu_pmu_of_match), 80562306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(dsu_pmu_acpi_match), 80662306a36Sopenharmony_ci .suppress_bind_attrs = true, 80762306a36Sopenharmony_ci }, 80862306a36Sopenharmony_ci .probe = dsu_pmu_device_probe, 80962306a36Sopenharmony_ci .remove = dsu_pmu_device_remove, 81062306a36Sopenharmony_ci}; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic int dsu_pmu_cpu_online(unsigned int cpu, struct hlist_node *node) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = hlist_entry_safe(node, struct dsu_pmu, 81562306a36Sopenharmony_ci cpuhp_node); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (!cpumask_test_cpu(cpu, &dsu_pmu->associated_cpus)) 81862306a36Sopenharmony_ci return 0; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* If the PMU is already managed, there is nothing to do */ 82162306a36Sopenharmony_ci if (!cpumask_empty(&dsu_pmu->active_cpu)) 82262306a36Sopenharmony_ci return 0; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci dsu_pmu_init_pmu(dsu_pmu); 82562306a36Sopenharmony_ci dsu_pmu_set_active_cpu(cpu, dsu_pmu); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic int dsu_pmu_cpu_teardown(unsigned int cpu, struct hlist_node *node) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci int dst; 83362306a36Sopenharmony_ci struct dsu_pmu *dsu_pmu = hlist_entry_safe(node, struct dsu_pmu, 83462306a36Sopenharmony_ci cpuhp_node); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (!cpumask_test_and_clear_cpu(cpu, &dsu_pmu->active_cpu)) 83762306a36Sopenharmony_ci return 0; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci dst = dsu_pmu_get_online_cpu_any_but(dsu_pmu, cpu); 84062306a36Sopenharmony_ci /* If there are no active CPUs in the DSU, leave IRQ disabled */ 84162306a36Sopenharmony_ci if (dst >= nr_cpu_ids) 84262306a36Sopenharmony_ci return 0; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci perf_pmu_migrate_context(&dsu_pmu->pmu, cpu, dst); 84562306a36Sopenharmony_ci dsu_pmu_set_active_cpu(dst, dsu_pmu); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return 0; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic int __init dsu_pmu_init(void) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci int ret; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, 85562306a36Sopenharmony_ci DRVNAME, 85662306a36Sopenharmony_ci dsu_pmu_cpu_online, 85762306a36Sopenharmony_ci dsu_pmu_cpu_teardown); 85862306a36Sopenharmony_ci if (ret < 0) 85962306a36Sopenharmony_ci return ret; 86062306a36Sopenharmony_ci dsu_pmu_cpuhp_state = ret; 86162306a36Sopenharmony_ci ret = platform_driver_register(&dsu_pmu_driver); 86262306a36Sopenharmony_ci if (ret) 86362306a36Sopenharmony_ci cpuhp_remove_multi_state(dsu_pmu_cpuhp_state); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return ret; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic void __exit dsu_pmu_exit(void) 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci platform_driver_unregister(&dsu_pmu_driver); 87162306a36Sopenharmony_ci cpuhp_remove_multi_state(dsu_pmu_cpuhp_state); 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cimodule_init(dsu_pmu_init); 87562306a36Sopenharmony_cimodule_exit(dsu_pmu_exit); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ciMODULE_DESCRIPTION("Perf driver for ARM DynamIQ Shared Unit"); 87862306a36Sopenharmony_ciMODULE_AUTHOR("Suzuki K Poulose <suzuki.poulose@arm.com>"); 87962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 880