162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// CCI Cache Coherent Interconnect PMU driver 362306a36Sopenharmony_ci// Copyright (C) 2013-2018 Arm Ltd. 462306a36Sopenharmony_ci// Author: Punit Agrawal <punit.agrawal@arm.com>, Suzuki Poulose <suzuki.poulose@arm.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/arm-cci.h> 762306a36Sopenharmony_ci#include <linux/io.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/perf_event.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/spinlock.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define DRIVER_NAME "ARM-CCI PMU" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define CCI_PMCR 0x0100 1962306a36Sopenharmony_ci#define CCI_PID2 0x0fe8 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define CCI_PMCR_CEN 0x00000001 2262306a36Sopenharmony_ci#define CCI_PMCR_NCNT_MASK 0x0000f800 2362306a36Sopenharmony_ci#define CCI_PMCR_NCNT_SHIFT 11 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define CCI_PID2_REV_MASK 0xf0 2662306a36Sopenharmony_ci#define CCI_PID2_REV_SHIFT 4 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define CCI_PMU_EVT_SEL 0x000 2962306a36Sopenharmony_ci#define CCI_PMU_CNTR 0x004 3062306a36Sopenharmony_ci#define CCI_PMU_CNTR_CTRL 0x008 3162306a36Sopenharmony_ci#define CCI_PMU_OVRFLW 0x00c 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define CCI_PMU_OVRFLW_FLAG 1 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define CCI_PMU_CNTR_SIZE(model) ((model)->cntr_size) 3662306a36Sopenharmony_ci#define CCI_PMU_CNTR_BASE(model, idx) ((idx) * CCI_PMU_CNTR_SIZE(model)) 3762306a36Sopenharmony_ci#define CCI_PMU_CNTR_MASK ((1ULL << 32) - 1) 3862306a36Sopenharmony_ci#define CCI_PMU_CNTR_LAST(cci_pmu) (cci_pmu->num_cntrs - 1) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define CCI_PMU_MAX_HW_CNTRS(model) \ 4162306a36Sopenharmony_ci ((model)->num_hw_cntrs + (model)->fixed_hw_cntrs) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Types of interfaces that can generate events */ 4462306a36Sopenharmony_cienum { 4562306a36Sopenharmony_ci CCI_IF_SLAVE, 4662306a36Sopenharmony_ci CCI_IF_MASTER, 4762306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI5xx_PMU 4862306a36Sopenharmony_ci CCI_IF_GLOBAL, 4962306a36Sopenharmony_ci#endif 5062306a36Sopenharmony_ci CCI_IF_MAX, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define NUM_HW_CNTRS_CII_4XX 4 5462306a36Sopenharmony_ci#define NUM_HW_CNTRS_CII_5XX 8 5562306a36Sopenharmony_ci#define NUM_HW_CNTRS_MAX NUM_HW_CNTRS_CII_5XX 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define FIXED_HW_CNTRS_CII_4XX 1 5862306a36Sopenharmony_ci#define FIXED_HW_CNTRS_CII_5XX 0 5962306a36Sopenharmony_ci#define FIXED_HW_CNTRS_MAX FIXED_HW_CNTRS_CII_4XX 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define HW_CNTRS_MAX (NUM_HW_CNTRS_MAX + FIXED_HW_CNTRS_MAX) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct event_range { 6462306a36Sopenharmony_ci u32 min; 6562306a36Sopenharmony_ci u32 max; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct cci_pmu_hw_events { 6962306a36Sopenharmony_ci struct perf_event **events; 7062306a36Sopenharmony_ci unsigned long *used_mask; 7162306a36Sopenharmony_ci raw_spinlock_t pmu_lock; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct cci_pmu; 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * struct cci_pmu_model: 7762306a36Sopenharmony_ci * @fixed_hw_cntrs - Number of fixed event counters 7862306a36Sopenharmony_ci * @num_hw_cntrs - Maximum number of programmable event counters 7962306a36Sopenharmony_ci * @cntr_size - Size of an event counter mapping 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistruct cci_pmu_model { 8262306a36Sopenharmony_ci char *name; 8362306a36Sopenharmony_ci u32 fixed_hw_cntrs; 8462306a36Sopenharmony_ci u32 num_hw_cntrs; 8562306a36Sopenharmony_ci u32 cntr_size; 8662306a36Sopenharmony_ci struct attribute **format_attrs; 8762306a36Sopenharmony_ci struct attribute **event_attrs; 8862306a36Sopenharmony_ci struct event_range event_ranges[CCI_IF_MAX]; 8962306a36Sopenharmony_ci int (*validate_hw_event)(struct cci_pmu *, unsigned long); 9062306a36Sopenharmony_ci int (*get_event_idx)(struct cci_pmu *, struct cci_pmu_hw_events *, unsigned long); 9162306a36Sopenharmony_ci void (*write_counters)(struct cci_pmu *, unsigned long *); 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic struct cci_pmu_model cci_pmu_models[]; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct cci_pmu { 9762306a36Sopenharmony_ci void __iomem *base; 9862306a36Sopenharmony_ci void __iomem *ctrl_base; 9962306a36Sopenharmony_ci struct pmu pmu; 10062306a36Sopenharmony_ci int cpu; 10162306a36Sopenharmony_ci int nr_irqs; 10262306a36Sopenharmony_ci int *irqs; 10362306a36Sopenharmony_ci unsigned long active_irqs; 10462306a36Sopenharmony_ci const struct cci_pmu_model *model; 10562306a36Sopenharmony_ci struct cci_pmu_hw_events hw_events; 10662306a36Sopenharmony_ci struct platform_device *plat_device; 10762306a36Sopenharmony_ci int num_cntrs; 10862306a36Sopenharmony_ci atomic_t active_events; 10962306a36Sopenharmony_ci struct mutex reserve_mutex; 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#define to_cci_pmu(c) (container_of(c, struct cci_pmu, pmu)) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic struct cci_pmu *g_cci_pmu; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cienum cci_models { 11762306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI400_PMU 11862306a36Sopenharmony_ci CCI400_R0, 11962306a36Sopenharmony_ci CCI400_R1, 12062306a36Sopenharmony_ci#endif 12162306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI5xx_PMU 12262306a36Sopenharmony_ci CCI500_R0, 12362306a36Sopenharmony_ci CCI550_R0, 12462306a36Sopenharmony_ci#endif 12562306a36Sopenharmony_ci CCI_MODEL_MAX 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void pmu_write_counters(struct cci_pmu *cci_pmu, 12962306a36Sopenharmony_ci unsigned long *mask); 13062306a36Sopenharmony_cistatic ssize_t __maybe_unused cci_pmu_format_show(struct device *dev, 13162306a36Sopenharmony_ci struct device_attribute *attr, char *buf); 13262306a36Sopenharmony_cistatic ssize_t __maybe_unused cci_pmu_event_show(struct device *dev, 13362306a36Sopenharmony_ci struct device_attribute *attr, char *buf); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#define CCI_EXT_ATTR_ENTRY(_name, _func, _config) \ 13662306a36Sopenharmony_ci &((struct dev_ext_attribute[]) { \ 13762306a36Sopenharmony_ci { __ATTR(_name, S_IRUGO, _func, NULL), (void *)_config } \ 13862306a36Sopenharmony_ci })[0].attr.attr 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci#define CCI_FORMAT_EXT_ATTR_ENTRY(_name, _config) \ 14162306a36Sopenharmony_ci CCI_EXT_ATTR_ENTRY(_name, cci_pmu_format_show, (char *)_config) 14262306a36Sopenharmony_ci#define CCI_EVENT_EXT_ATTR_ENTRY(_name, _config) \ 14362306a36Sopenharmony_ci CCI_EXT_ATTR_ENTRY(_name, cci_pmu_event_show, (unsigned long)_config) 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* CCI400 PMU Specific definitions */ 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI400_PMU 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* Port ids */ 15062306a36Sopenharmony_ci#define CCI400_PORT_S0 0 15162306a36Sopenharmony_ci#define CCI400_PORT_S1 1 15262306a36Sopenharmony_ci#define CCI400_PORT_S2 2 15362306a36Sopenharmony_ci#define CCI400_PORT_S3 3 15462306a36Sopenharmony_ci#define CCI400_PORT_S4 4 15562306a36Sopenharmony_ci#define CCI400_PORT_M0 5 15662306a36Sopenharmony_ci#define CCI400_PORT_M1 6 15762306a36Sopenharmony_ci#define CCI400_PORT_M2 7 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#define CCI400_R1_PX 5 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* 16262306a36Sopenharmony_ci * Instead of an event id to monitor CCI cycles, a dedicated counter is 16362306a36Sopenharmony_ci * provided. Use 0xff to represent CCI cycles and hope that no future revisions 16462306a36Sopenharmony_ci * make use of this event in hardware. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cienum cci400_perf_events { 16762306a36Sopenharmony_ci CCI400_PMU_CYCLES = 0xff 16862306a36Sopenharmony_ci}; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci#define CCI400_PMU_CYCLE_CNTR_IDX 0 17162306a36Sopenharmony_ci#define CCI400_PMU_CNTR0_IDX 1 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/* 17462306a36Sopenharmony_ci * CCI PMU event id is an 8-bit value made of two parts - bits 7:5 for one of 8 17562306a36Sopenharmony_ci * ports and bits 4:0 are event codes. There are different event codes 17662306a36Sopenharmony_ci * associated with each port type. 17762306a36Sopenharmony_ci * 17862306a36Sopenharmony_ci * Additionally, the range of events associated with the port types changed 17962306a36Sopenharmony_ci * between Rev0 and Rev1. 18062306a36Sopenharmony_ci * 18162306a36Sopenharmony_ci * The constants below define the range of valid codes for each port type for 18262306a36Sopenharmony_ci * the different revisions and are used to validate the event to be monitored. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci#define CCI400_PMU_EVENT_MASK 0xffUL 18662306a36Sopenharmony_ci#define CCI400_PMU_EVENT_SOURCE_SHIFT 5 18762306a36Sopenharmony_ci#define CCI400_PMU_EVENT_SOURCE_MASK 0x7 18862306a36Sopenharmony_ci#define CCI400_PMU_EVENT_CODE_SHIFT 0 18962306a36Sopenharmony_ci#define CCI400_PMU_EVENT_CODE_MASK 0x1f 19062306a36Sopenharmony_ci#define CCI400_PMU_EVENT_SOURCE(event) \ 19162306a36Sopenharmony_ci ((event >> CCI400_PMU_EVENT_SOURCE_SHIFT) & \ 19262306a36Sopenharmony_ci CCI400_PMU_EVENT_SOURCE_MASK) 19362306a36Sopenharmony_ci#define CCI400_PMU_EVENT_CODE(event) \ 19462306a36Sopenharmony_ci ((event >> CCI400_PMU_EVENT_CODE_SHIFT) & CCI400_PMU_EVENT_CODE_MASK) 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci#define CCI400_R0_SLAVE_PORT_MIN_EV 0x00 19762306a36Sopenharmony_ci#define CCI400_R0_SLAVE_PORT_MAX_EV 0x13 19862306a36Sopenharmony_ci#define CCI400_R0_MASTER_PORT_MIN_EV 0x14 19962306a36Sopenharmony_ci#define CCI400_R0_MASTER_PORT_MAX_EV 0x1a 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci#define CCI400_R1_SLAVE_PORT_MIN_EV 0x00 20262306a36Sopenharmony_ci#define CCI400_R1_SLAVE_PORT_MAX_EV 0x14 20362306a36Sopenharmony_ci#define CCI400_R1_MASTER_PORT_MIN_EV 0x00 20462306a36Sopenharmony_ci#define CCI400_R1_MASTER_PORT_MAX_EV 0x11 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci#define CCI400_CYCLE_EVENT_EXT_ATTR_ENTRY(_name, _config) \ 20762306a36Sopenharmony_ci CCI_EXT_ATTR_ENTRY(_name, cci400_pmu_cycle_event_show, \ 20862306a36Sopenharmony_ci (unsigned long)_config) 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic ssize_t cci400_pmu_cycle_event_show(struct device *dev, 21162306a36Sopenharmony_ci struct device_attribute *attr, char *buf); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic struct attribute *cci400_pmu_format_attrs[] = { 21462306a36Sopenharmony_ci CCI_FORMAT_EXT_ATTR_ENTRY(event, "config:0-4"), 21562306a36Sopenharmony_ci CCI_FORMAT_EXT_ATTR_ENTRY(source, "config:5-7"), 21662306a36Sopenharmony_ci NULL 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic struct attribute *cci400_r0_pmu_event_attrs[] = { 22062306a36Sopenharmony_ci /* Slave events */ 22162306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_any, 0x0), 22262306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_device, 0x01), 22362306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_normal_or_nonshareable, 0x2), 22462306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_inner_or_outershareable, 0x3), 22562306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_cache_maintenance, 0x4), 22662306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_mem_barrier, 0x5), 22762306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_sync_barrier, 0x6), 22862306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg, 0x7), 22962306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg_sync, 0x8), 23062306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_stall_tt_full, 0x9), 23162306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_last_hs_snoop, 0xA), 23262306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_stall_rvalids_h_rready_l, 0xB), 23362306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_any, 0xC), 23462306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_device, 0xD), 23562306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_normal_or_nonshareable, 0xE), 23662306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_inner_or_outershare_wback_wclean, 0xF), 23762306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_write_unique, 0x10), 23862306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_write_line_unique, 0x11), 23962306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_evict, 0x12), 24062306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_stall_tt_full, 0x13), 24162306a36Sopenharmony_ci /* Master events */ 24262306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_retry_speculative_fetch, 0x14), 24362306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_addr_hazard, 0x15), 24462306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_id_hazard, 0x16), 24562306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_tt_full, 0x17), 24662306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_barrier_hazard, 0x18), 24762306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_barrier_hazard, 0x19), 24862306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_tt_full, 0x1A), 24962306a36Sopenharmony_ci /* Special event for cycles counter */ 25062306a36Sopenharmony_ci CCI400_CYCLE_EVENT_EXT_ATTR_ENTRY(cycles, 0xff), 25162306a36Sopenharmony_ci NULL 25262306a36Sopenharmony_ci}; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic struct attribute *cci400_r1_pmu_event_attrs[] = { 25562306a36Sopenharmony_ci /* Slave events */ 25662306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_any, 0x0), 25762306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_device, 0x01), 25862306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_normal_or_nonshareable, 0x2), 25962306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_inner_or_outershareable, 0x3), 26062306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_cache_maintenance, 0x4), 26162306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_mem_barrier, 0x5), 26262306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_sync_barrier, 0x6), 26362306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg, 0x7), 26462306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg_sync, 0x8), 26562306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_stall_tt_full, 0x9), 26662306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_last_hs_snoop, 0xA), 26762306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_stall_rvalids_h_rready_l, 0xB), 26862306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_any, 0xC), 26962306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_device, 0xD), 27062306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_normal_or_nonshareable, 0xE), 27162306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_inner_or_outershare_wback_wclean, 0xF), 27262306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_write_unique, 0x10), 27362306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_write_line_unique, 0x11), 27462306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_evict, 0x12), 27562306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_stall_tt_full, 0x13), 27662306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_stall_slave_id_hazard, 0x14), 27762306a36Sopenharmony_ci /* Master events */ 27862306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_retry_speculative_fetch, 0x0), 27962306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_stall_cycle_addr_hazard, 0x1), 28062306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_master_id_hazard, 0x2), 28162306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_hi_prio_rtq_full, 0x3), 28262306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_barrier_hazard, 0x4), 28362306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_barrier_hazard, 0x5), 28462306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_wtq_full, 0x6), 28562306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_low_prio_rtq_full, 0x7), 28662306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_mid_prio_rtq_full, 0x8), 28762306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_qvn_vn0, 0x9), 28862306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_qvn_vn1, 0xA), 28962306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_qvn_vn2, 0xB), 29062306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_qvn_vn3, 0xC), 29162306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_qvn_vn0, 0xD), 29262306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_qvn_vn1, 0xE), 29362306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_qvn_vn2, 0xF), 29462306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_qvn_vn3, 0x10), 29562306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_unique_or_line_unique_addr_hazard, 0x11), 29662306a36Sopenharmony_ci /* Special event for cycles counter */ 29762306a36Sopenharmony_ci CCI400_CYCLE_EVENT_EXT_ATTR_ENTRY(cycles, 0xff), 29862306a36Sopenharmony_ci NULL 29962306a36Sopenharmony_ci}; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic ssize_t cci400_pmu_cycle_event_show(struct device *dev, 30262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct dev_ext_attribute *eattr = container_of(attr, 30562306a36Sopenharmony_ci struct dev_ext_attribute, attr); 30662306a36Sopenharmony_ci return sysfs_emit(buf, "config=0x%lx\n", (unsigned long)eattr->var); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int cci400_get_event_idx(struct cci_pmu *cci_pmu, 31062306a36Sopenharmony_ci struct cci_pmu_hw_events *hw, 31162306a36Sopenharmony_ci unsigned long cci_event) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci int idx; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* cycles event idx is fixed */ 31662306a36Sopenharmony_ci if (cci_event == CCI400_PMU_CYCLES) { 31762306a36Sopenharmony_ci if (test_and_set_bit(CCI400_PMU_CYCLE_CNTR_IDX, hw->used_mask)) 31862306a36Sopenharmony_ci return -EAGAIN; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return CCI400_PMU_CYCLE_CNTR_IDX; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci for (idx = CCI400_PMU_CNTR0_IDX; idx <= CCI_PMU_CNTR_LAST(cci_pmu); ++idx) 32462306a36Sopenharmony_ci if (!test_and_set_bit(idx, hw->used_mask)) 32562306a36Sopenharmony_ci return idx; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* No counters available */ 32862306a36Sopenharmony_ci return -EAGAIN; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int cci400_validate_hw_event(struct cci_pmu *cci_pmu, unsigned long hw_event) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci u8 ev_source = CCI400_PMU_EVENT_SOURCE(hw_event); 33462306a36Sopenharmony_ci u8 ev_code = CCI400_PMU_EVENT_CODE(hw_event); 33562306a36Sopenharmony_ci int if_type; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (hw_event & ~CCI400_PMU_EVENT_MASK) 33862306a36Sopenharmony_ci return -ENOENT; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (hw_event == CCI400_PMU_CYCLES) 34162306a36Sopenharmony_ci return hw_event; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci switch (ev_source) { 34462306a36Sopenharmony_ci case CCI400_PORT_S0: 34562306a36Sopenharmony_ci case CCI400_PORT_S1: 34662306a36Sopenharmony_ci case CCI400_PORT_S2: 34762306a36Sopenharmony_ci case CCI400_PORT_S3: 34862306a36Sopenharmony_ci case CCI400_PORT_S4: 34962306a36Sopenharmony_ci /* Slave Interface */ 35062306a36Sopenharmony_ci if_type = CCI_IF_SLAVE; 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci case CCI400_PORT_M0: 35362306a36Sopenharmony_ci case CCI400_PORT_M1: 35462306a36Sopenharmony_ci case CCI400_PORT_M2: 35562306a36Sopenharmony_ci /* Master Interface */ 35662306a36Sopenharmony_ci if_type = CCI_IF_MASTER; 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci default: 35962306a36Sopenharmony_ci return -ENOENT; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (ev_code >= cci_pmu->model->event_ranges[if_type].min && 36362306a36Sopenharmony_ci ev_code <= cci_pmu->model->event_ranges[if_type].max) 36462306a36Sopenharmony_ci return hw_event; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return -ENOENT; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int probe_cci400_revision(struct cci_pmu *cci_pmu) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci int rev; 37262306a36Sopenharmony_ci rev = readl_relaxed(cci_pmu->ctrl_base + CCI_PID2) & CCI_PID2_REV_MASK; 37362306a36Sopenharmony_ci rev >>= CCI_PID2_REV_SHIFT; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (rev < CCI400_R1_PX) 37662306a36Sopenharmony_ci return CCI400_R0; 37762306a36Sopenharmony_ci else 37862306a36Sopenharmony_ci return CCI400_R1; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic const struct cci_pmu_model *probe_cci_model(struct cci_pmu *cci_pmu) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci if (platform_has_secure_cci_access()) 38462306a36Sopenharmony_ci return &cci_pmu_models[probe_cci400_revision(cci_pmu)]; 38562306a36Sopenharmony_ci return NULL; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci#else /* !CONFIG_ARM_CCI400_PMU */ 38862306a36Sopenharmony_cistatic inline struct cci_pmu_model *probe_cci_model(struct cci_pmu *cci_pmu) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci return NULL; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci#endif /* CONFIG_ARM_CCI400_PMU */ 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI5xx_PMU 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/* 39762306a36Sopenharmony_ci * CCI5xx PMU event id is an 9-bit value made of two parts. 39862306a36Sopenharmony_ci * bits [8:5] - Source for the event 39962306a36Sopenharmony_ci * bits [4:0] - Event code (specific to type of interface) 40062306a36Sopenharmony_ci * 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci/* Port ids */ 40562306a36Sopenharmony_ci#define CCI5xx_PORT_S0 0x0 40662306a36Sopenharmony_ci#define CCI5xx_PORT_S1 0x1 40762306a36Sopenharmony_ci#define CCI5xx_PORT_S2 0x2 40862306a36Sopenharmony_ci#define CCI5xx_PORT_S3 0x3 40962306a36Sopenharmony_ci#define CCI5xx_PORT_S4 0x4 41062306a36Sopenharmony_ci#define CCI5xx_PORT_S5 0x5 41162306a36Sopenharmony_ci#define CCI5xx_PORT_S6 0x6 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci#define CCI5xx_PORT_M0 0x8 41462306a36Sopenharmony_ci#define CCI5xx_PORT_M1 0x9 41562306a36Sopenharmony_ci#define CCI5xx_PORT_M2 0xa 41662306a36Sopenharmony_ci#define CCI5xx_PORT_M3 0xb 41762306a36Sopenharmony_ci#define CCI5xx_PORT_M4 0xc 41862306a36Sopenharmony_ci#define CCI5xx_PORT_M5 0xd 41962306a36Sopenharmony_ci#define CCI5xx_PORT_M6 0xe 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci#define CCI5xx_PORT_GLOBAL 0xf 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci#define CCI5xx_PMU_EVENT_MASK 0x1ffUL 42462306a36Sopenharmony_ci#define CCI5xx_PMU_EVENT_SOURCE_SHIFT 0x5 42562306a36Sopenharmony_ci#define CCI5xx_PMU_EVENT_SOURCE_MASK 0xf 42662306a36Sopenharmony_ci#define CCI5xx_PMU_EVENT_CODE_SHIFT 0x0 42762306a36Sopenharmony_ci#define CCI5xx_PMU_EVENT_CODE_MASK 0x1f 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci#define CCI5xx_PMU_EVENT_SOURCE(event) \ 43062306a36Sopenharmony_ci ((event >> CCI5xx_PMU_EVENT_SOURCE_SHIFT) & CCI5xx_PMU_EVENT_SOURCE_MASK) 43162306a36Sopenharmony_ci#define CCI5xx_PMU_EVENT_CODE(event) \ 43262306a36Sopenharmony_ci ((event >> CCI5xx_PMU_EVENT_CODE_SHIFT) & CCI5xx_PMU_EVENT_CODE_MASK) 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci#define CCI5xx_SLAVE_PORT_MIN_EV 0x00 43562306a36Sopenharmony_ci#define CCI5xx_SLAVE_PORT_MAX_EV 0x1f 43662306a36Sopenharmony_ci#define CCI5xx_MASTER_PORT_MIN_EV 0x00 43762306a36Sopenharmony_ci#define CCI5xx_MASTER_PORT_MAX_EV 0x06 43862306a36Sopenharmony_ci#define CCI5xx_GLOBAL_PORT_MIN_EV 0x00 43962306a36Sopenharmony_ci#define CCI5xx_GLOBAL_PORT_MAX_EV 0x0f 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci#define CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(_name, _config) \ 44362306a36Sopenharmony_ci CCI_EXT_ATTR_ENTRY(_name, cci5xx_pmu_global_event_show, \ 44462306a36Sopenharmony_ci (unsigned long) _config) 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic ssize_t cci5xx_pmu_global_event_show(struct device *dev, 44762306a36Sopenharmony_ci struct device_attribute *attr, char *buf); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic struct attribute *cci5xx_pmu_format_attrs[] = { 45062306a36Sopenharmony_ci CCI_FORMAT_EXT_ATTR_ENTRY(event, "config:0-4"), 45162306a36Sopenharmony_ci CCI_FORMAT_EXT_ATTR_ENTRY(source, "config:5-8"), 45262306a36Sopenharmony_ci NULL, 45362306a36Sopenharmony_ci}; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic struct attribute *cci5xx_pmu_event_attrs[] = { 45662306a36Sopenharmony_ci /* Slave events */ 45762306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_arvalid, 0x0), 45862306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_dev, 0x1), 45962306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_nonshareable, 0x2), 46062306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_shareable_non_alloc, 0x3), 46162306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_shareable_alloc, 0x4), 46262306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_invalidate, 0x5), 46362306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_cache_maint, 0x6), 46462306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg, 0x7), 46562306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_rval, 0x8), 46662306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_rlast_snoop, 0x9), 46762306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_awalid, 0xA), 46862306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_dev, 0xB), 46962306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_non_shareable, 0xC), 47062306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_share_wb, 0xD), 47162306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_share_wlu, 0xE), 47262306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_share_wunique, 0xF), 47362306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_evict, 0x10), 47462306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_wrevict, 0x11), 47562306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_w_data_beat, 0x12), 47662306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_srq_acvalid, 0x13), 47762306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_srq_read, 0x14), 47862306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_srq_clean, 0x15), 47962306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_srq_data_transfer_low, 0x16), 48062306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_stall_arvalid, 0x17), 48162306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_stall, 0x18), 48262306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_stall, 0x19), 48362306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_w_data_stall, 0x1A), 48462306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_w_resp_stall, 0x1B), 48562306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_srq_stall, 0x1C), 48662306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_s_data_stall, 0x1D), 48762306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_rq_stall_ot_limit, 0x1E), 48862306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(si_r_stall_arbit, 0x1F), 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* Master events */ 49162306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_r_data_beat_any, 0x0), 49262306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_w_data_beat_any, 0x1), 49362306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall, 0x2), 49462306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_r_data_stall, 0x3), 49562306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall, 0x4), 49662306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_w_data_stall, 0x5), 49762306a36Sopenharmony_ci CCI_EVENT_EXT_ATTR_ENTRY(mi_w_resp_stall, 0x6), 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* Global events */ 50062306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_filter_bank_0_1, 0x0), 50162306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_filter_bank_2_3, 0x1), 50262306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_filter_bank_4_5, 0x2), 50362306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_filter_bank_6_7, 0x3), 50462306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_miss_filter_bank_0_1, 0x4), 50562306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_miss_filter_bank_2_3, 0x5), 50662306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_miss_filter_bank_4_5, 0x6), 50762306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_miss_filter_bank_6_7, 0x7), 50862306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_back_invalidation, 0x8), 50962306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_stall_alloc_busy, 0x9), 51062306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_stall_tt_full, 0xA), 51162306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_wrq, 0xB), 51262306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_cd_hs, 0xC), 51362306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_rq_stall_addr_hazard, 0xD), 51462306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_rq_stall_tt_full, 0xE), 51562306a36Sopenharmony_ci CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_rq_tzmp1_prot, 0xF), 51662306a36Sopenharmony_ci NULL 51762306a36Sopenharmony_ci}; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic ssize_t cci5xx_pmu_global_event_show(struct device *dev, 52062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct dev_ext_attribute *eattr = container_of(attr, 52362306a36Sopenharmony_ci struct dev_ext_attribute, attr); 52462306a36Sopenharmony_ci /* Global events have single fixed source code */ 52562306a36Sopenharmony_ci return sysfs_emit(buf, "event=0x%lx,source=0x%x\n", 52662306a36Sopenharmony_ci (unsigned long)eattr->var, CCI5xx_PORT_GLOBAL); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/* 53062306a36Sopenharmony_ci * CCI500 provides 8 independent event counters that can count 53162306a36Sopenharmony_ci * any of the events available. 53262306a36Sopenharmony_ci * CCI500 PMU event source ids 53362306a36Sopenharmony_ci * 0x0-0x6 - Slave interfaces 53462306a36Sopenharmony_ci * 0x8-0xD - Master interfaces 53562306a36Sopenharmony_ci * 0xf - Global Events 53662306a36Sopenharmony_ci * 0x7,0xe - Reserved 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_cistatic int cci500_validate_hw_event(struct cci_pmu *cci_pmu, 53962306a36Sopenharmony_ci unsigned long hw_event) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci u32 ev_source = CCI5xx_PMU_EVENT_SOURCE(hw_event); 54262306a36Sopenharmony_ci u32 ev_code = CCI5xx_PMU_EVENT_CODE(hw_event); 54362306a36Sopenharmony_ci int if_type; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (hw_event & ~CCI5xx_PMU_EVENT_MASK) 54662306a36Sopenharmony_ci return -ENOENT; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci switch (ev_source) { 54962306a36Sopenharmony_ci case CCI5xx_PORT_S0: 55062306a36Sopenharmony_ci case CCI5xx_PORT_S1: 55162306a36Sopenharmony_ci case CCI5xx_PORT_S2: 55262306a36Sopenharmony_ci case CCI5xx_PORT_S3: 55362306a36Sopenharmony_ci case CCI5xx_PORT_S4: 55462306a36Sopenharmony_ci case CCI5xx_PORT_S5: 55562306a36Sopenharmony_ci case CCI5xx_PORT_S6: 55662306a36Sopenharmony_ci if_type = CCI_IF_SLAVE; 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci case CCI5xx_PORT_M0: 55962306a36Sopenharmony_ci case CCI5xx_PORT_M1: 56062306a36Sopenharmony_ci case CCI5xx_PORT_M2: 56162306a36Sopenharmony_ci case CCI5xx_PORT_M3: 56262306a36Sopenharmony_ci case CCI5xx_PORT_M4: 56362306a36Sopenharmony_ci case CCI5xx_PORT_M5: 56462306a36Sopenharmony_ci if_type = CCI_IF_MASTER; 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci case CCI5xx_PORT_GLOBAL: 56762306a36Sopenharmony_ci if_type = CCI_IF_GLOBAL; 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci default: 57062306a36Sopenharmony_ci return -ENOENT; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (ev_code >= cci_pmu->model->event_ranges[if_type].min && 57462306a36Sopenharmony_ci ev_code <= cci_pmu->model->event_ranges[if_type].max) 57562306a36Sopenharmony_ci return hw_event; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return -ENOENT; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci/* 58162306a36Sopenharmony_ci * CCI550 provides 8 independent event counters that can count 58262306a36Sopenharmony_ci * any of the events available. 58362306a36Sopenharmony_ci * CCI550 PMU event source ids 58462306a36Sopenharmony_ci * 0x0-0x6 - Slave interfaces 58562306a36Sopenharmony_ci * 0x8-0xe - Master interfaces 58662306a36Sopenharmony_ci * 0xf - Global Events 58762306a36Sopenharmony_ci * 0x7 - Reserved 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_cistatic int cci550_validate_hw_event(struct cci_pmu *cci_pmu, 59062306a36Sopenharmony_ci unsigned long hw_event) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci u32 ev_source = CCI5xx_PMU_EVENT_SOURCE(hw_event); 59362306a36Sopenharmony_ci u32 ev_code = CCI5xx_PMU_EVENT_CODE(hw_event); 59462306a36Sopenharmony_ci int if_type; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (hw_event & ~CCI5xx_PMU_EVENT_MASK) 59762306a36Sopenharmony_ci return -ENOENT; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci switch (ev_source) { 60062306a36Sopenharmony_ci case CCI5xx_PORT_S0: 60162306a36Sopenharmony_ci case CCI5xx_PORT_S1: 60262306a36Sopenharmony_ci case CCI5xx_PORT_S2: 60362306a36Sopenharmony_ci case CCI5xx_PORT_S3: 60462306a36Sopenharmony_ci case CCI5xx_PORT_S4: 60562306a36Sopenharmony_ci case CCI5xx_PORT_S5: 60662306a36Sopenharmony_ci case CCI5xx_PORT_S6: 60762306a36Sopenharmony_ci if_type = CCI_IF_SLAVE; 60862306a36Sopenharmony_ci break; 60962306a36Sopenharmony_ci case CCI5xx_PORT_M0: 61062306a36Sopenharmony_ci case CCI5xx_PORT_M1: 61162306a36Sopenharmony_ci case CCI5xx_PORT_M2: 61262306a36Sopenharmony_ci case CCI5xx_PORT_M3: 61362306a36Sopenharmony_ci case CCI5xx_PORT_M4: 61462306a36Sopenharmony_ci case CCI5xx_PORT_M5: 61562306a36Sopenharmony_ci case CCI5xx_PORT_M6: 61662306a36Sopenharmony_ci if_type = CCI_IF_MASTER; 61762306a36Sopenharmony_ci break; 61862306a36Sopenharmony_ci case CCI5xx_PORT_GLOBAL: 61962306a36Sopenharmony_ci if_type = CCI_IF_GLOBAL; 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci default: 62262306a36Sopenharmony_ci return -ENOENT; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (ev_code >= cci_pmu->model->event_ranges[if_type].min && 62662306a36Sopenharmony_ci ev_code <= cci_pmu->model->event_ranges[if_type].max) 62762306a36Sopenharmony_ci return hw_event; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return -ENOENT; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci#endif /* CONFIG_ARM_CCI5xx_PMU */ 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci/* 63562306a36Sopenharmony_ci * Program the CCI PMU counters which have PERF_HES_ARCH set 63662306a36Sopenharmony_ci * with the event period and mark them ready before we enable 63762306a36Sopenharmony_ci * PMU. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_cistatic void cci_pmu_sync_counters(struct cci_pmu *cci_pmu) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci int i; 64262306a36Sopenharmony_ci struct cci_pmu_hw_events *cci_hw = &cci_pmu->hw_events; 64362306a36Sopenharmony_ci DECLARE_BITMAP(mask, HW_CNTRS_MAX); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci bitmap_zero(mask, HW_CNTRS_MAX); 64662306a36Sopenharmony_ci for_each_set_bit(i, cci_pmu->hw_events.used_mask, cci_pmu->num_cntrs) { 64762306a36Sopenharmony_ci struct perf_event *event = cci_hw->events[i]; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (WARN_ON(!event)) 65062306a36Sopenharmony_ci continue; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* Leave the events which are not counting */ 65362306a36Sopenharmony_ci if (event->hw.state & PERF_HES_STOPPED) 65462306a36Sopenharmony_ci continue; 65562306a36Sopenharmony_ci if (event->hw.state & PERF_HES_ARCH) { 65662306a36Sopenharmony_ci __set_bit(i, mask); 65762306a36Sopenharmony_ci event->hw.state &= ~PERF_HES_ARCH; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci pmu_write_counters(cci_pmu, mask); 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci/* Should be called with cci_pmu->hw_events->pmu_lock held */ 66562306a36Sopenharmony_cistatic void __cci_pmu_enable_nosync(struct cci_pmu *cci_pmu) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci u32 val; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* Enable all the PMU counters. */ 67062306a36Sopenharmony_ci val = readl_relaxed(cci_pmu->ctrl_base + CCI_PMCR) | CCI_PMCR_CEN; 67162306a36Sopenharmony_ci writel(val, cci_pmu->ctrl_base + CCI_PMCR); 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci/* Should be called with cci_pmu->hw_events->pmu_lock held */ 67562306a36Sopenharmony_cistatic void __cci_pmu_enable_sync(struct cci_pmu *cci_pmu) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci cci_pmu_sync_counters(cci_pmu); 67862306a36Sopenharmony_ci __cci_pmu_enable_nosync(cci_pmu); 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci/* Should be called with cci_pmu->hw_events->pmu_lock held */ 68262306a36Sopenharmony_cistatic void __cci_pmu_disable(struct cci_pmu *cci_pmu) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci u32 val; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Disable all the PMU counters. */ 68762306a36Sopenharmony_ci val = readl_relaxed(cci_pmu->ctrl_base + CCI_PMCR) & ~CCI_PMCR_CEN; 68862306a36Sopenharmony_ci writel(val, cci_pmu->ctrl_base + CCI_PMCR); 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic ssize_t cci_pmu_format_show(struct device *dev, 69262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct dev_ext_attribute *eattr = container_of(attr, 69562306a36Sopenharmony_ci struct dev_ext_attribute, attr); 69662306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", (char *)eattr->var); 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic ssize_t cci_pmu_event_show(struct device *dev, 70062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct dev_ext_attribute *eattr = container_of(attr, 70362306a36Sopenharmony_ci struct dev_ext_attribute, attr); 70462306a36Sopenharmony_ci /* source parameter is mandatory for normal PMU events */ 70562306a36Sopenharmony_ci return sysfs_emit(buf, "source=?,event=0x%lx\n", 70662306a36Sopenharmony_ci (unsigned long)eattr->var); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci return 0 <= idx && idx <= CCI_PMU_CNTR_LAST(cci_pmu); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic u32 pmu_read_register(struct cci_pmu *cci_pmu, int idx, unsigned int offset) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci return readl_relaxed(cci_pmu->base + 71762306a36Sopenharmony_ci CCI_PMU_CNTR_BASE(cci_pmu->model, idx) + offset); 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic void pmu_write_register(struct cci_pmu *cci_pmu, u32 value, 72162306a36Sopenharmony_ci int idx, unsigned int offset) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci writel_relaxed(value, cci_pmu->base + 72462306a36Sopenharmony_ci CCI_PMU_CNTR_BASE(cci_pmu->model, idx) + offset); 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic void pmu_disable_counter(struct cci_pmu *cci_pmu, int idx) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci pmu_write_register(cci_pmu, 0, idx, CCI_PMU_CNTR_CTRL); 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic void pmu_enable_counter(struct cci_pmu *cci_pmu, int idx) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci pmu_write_register(cci_pmu, 1, idx, CCI_PMU_CNTR_CTRL); 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic bool __maybe_unused 73862306a36Sopenharmony_cipmu_counter_is_enabled(struct cci_pmu *cci_pmu, int idx) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci return (pmu_read_register(cci_pmu, idx, CCI_PMU_CNTR_CTRL) & 0x1) != 0; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic void pmu_set_event(struct cci_pmu *cci_pmu, int idx, unsigned long event) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci pmu_write_register(cci_pmu, event, idx, CCI_PMU_EVT_SEL); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/* 74962306a36Sopenharmony_ci * For all counters on the CCI-PMU, disable any 'enabled' counters, 75062306a36Sopenharmony_ci * saving the changed counters in the mask, so that we can restore 75162306a36Sopenharmony_ci * it later using pmu_restore_counters. The mask is private to the 75262306a36Sopenharmony_ci * caller. We cannot rely on the used_mask maintained by the CCI_PMU 75362306a36Sopenharmony_ci * as it only tells us if the counter is assigned to perf_event or not. 75462306a36Sopenharmony_ci * The state of the perf_event cannot be locked by the PMU layer, hence 75562306a36Sopenharmony_ci * we check the individual counter status (which can be locked by 75662306a36Sopenharmony_ci * cci_pm->hw_events->pmu_lock). 75762306a36Sopenharmony_ci * 75862306a36Sopenharmony_ci * @mask should be initialised to empty by the caller. 75962306a36Sopenharmony_ci */ 76062306a36Sopenharmony_cistatic void __maybe_unused 76162306a36Sopenharmony_cipmu_save_counters(struct cci_pmu *cci_pmu, unsigned long *mask) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci int i; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci for (i = 0; i < cci_pmu->num_cntrs; i++) { 76662306a36Sopenharmony_ci if (pmu_counter_is_enabled(cci_pmu, i)) { 76762306a36Sopenharmony_ci set_bit(i, mask); 76862306a36Sopenharmony_ci pmu_disable_counter(cci_pmu, i); 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci/* 77462306a36Sopenharmony_ci * Restore the status of the counters. Reversal of the pmu_save_counters(). 77562306a36Sopenharmony_ci * For each counter set in the mask, enable the counter back. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_cistatic void __maybe_unused 77862306a36Sopenharmony_cipmu_restore_counters(struct cci_pmu *cci_pmu, unsigned long *mask) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci int i; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci for_each_set_bit(i, mask, cci_pmu->num_cntrs) 78362306a36Sopenharmony_ci pmu_enable_counter(cci_pmu, i); 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci/* 78762306a36Sopenharmony_ci * Returns the number of programmable counters actually implemented 78862306a36Sopenharmony_ci * by the cci 78962306a36Sopenharmony_ci */ 79062306a36Sopenharmony_cistatic u32 pmu_get_max_counters(struct cci_pmu *cci_pmu) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci return (readl_relaxed(cci_pmu->ctrl_base + CCI_PMCR) & 79362306a36Sopenharmony_ci CCI_PMCR_NCNT_MASK) >> CCI_PMCR_NCNT_SHIFT; 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic int pmu_get_event_idx(struct cci_pmu_hw_events *hw, struct perf_event *event) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); 79962306a36Sopenharmony_ci unsigned long cci_event = event->hw.config_base; 80062306a36Sopenharmony_ci int idx; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (cci_pmu->model->get_event_idx) 80362306a36Sopenharmony_ci return cci_pmu->model->get_event_idx(cci_pmu, hw, cci_event); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* Generic code to find an unused idx from the mask */ 80662306a36Sopenharmony_ci for (idx = 0; idx <= CCI_PMU_CNTR_LAST(cci_pmu); idx++) 80762306a36Sopenharmony_ci if (!test_and_set_bit(idx, hw->used_mask)) 80862306a36Sopenharmony_ci return idx; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* No counters available */ 81162306a36Sopenharmony_ci return -EAGAIN; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic int pmu_map_event(struct perf_event *event) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (event->attr.type < PERF_TYPE_MAX || 81962306a36Sopenharmony_ci !cci_pmu->model->validate_hw_event) 82062306a36Sopenharmony_ci return -ENOENT; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return cci_pmu->model->validate_hw_event(cci_pmu, event->attr.config); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int pmu_request_irq(struct cci_pmu *cci_pmu, irq_handler_t handler) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci int i; 82862306a36Sopenharmony_ci struct platform_device *pmu_device = cci_pmu->plat_device; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (unlikely(!pmu_device)) 83162306a36Sopenharmony_ci return -ENODEV; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (cci_pmu->nr_irqs < 1) { 83462306a36Sopenharmony_ci dev_err(&pmu_device->dev, "no irqs for CCI PMUs defined\n"); 83562306a36Sopenharmony_ci return -ENODEV; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* 83962306a36Sopenharmony_ci * Register all available CCI PMU interrupts. In the interrupt handler 84062306a36Sopenharmony_ci * we iterate over the counters checking for interrupt source (the 84162306a36Sopenharmony_ci * overflowing counter) and clear it. 84262306a36Sopenharmony_ci * 84362306a36Sopenharmony_ci * This should allow handling of non-unique interrupt for the counters. 84462306a36Sopenharmony_ci */ 84562306a36Sopenharmony_ci for (i = 0; i < cci_pmu->nr_irqs; i++) { 84662306a36Sopenharmony_ci int err = request_irq(cci_pmu->irqs[i], handler, IRQF_SHARED, 84762306a36Sopenharmony_ci "arm-cci-pmu", cci_pmu); 84862306a36Sopenharmony_ci if (err) { 84962306a36Sopenharmony_ci dev_err(&pmu_device->dev, "unable to request IRQ%d for ARM CCI PMU counters\n", 85062306a36Sopenharmony_ci cci_pmu->irqs[i]); 85162306a36Sopenharmony_ci return err; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci set_bit(i, &cci_pmu->active_irqs); 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return 0; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic void pmu_free_irq(struct cci_pmu *cci_pmu) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci int i; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci for (i = 0; i < cci_pmu->nr_irqs; i++) { 86562306a36Sopenharmony_ci if (!test_and_clear_bit(i, &cci_pmu->active_irqs)) 86662306a36Sopenharmony_ci continue; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci free_irq(cci_pmu->irqs[i], cci_pmu); 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic u32 pmu_read_counter(struct perf_event *event) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); 87562306a36Sopenharmony_ci struct hw_perf_event *hw_counter = &event->hw; 87662306a36Sopenharmony_ci int idx = hw_counter->idx; 87762306a36Sopenharmony_ci u32 value; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { 88062306a36Sopenharmony_ci dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); 88162306a36Sopenharmony_ci return 0; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci value = pmu_read_register(cci_pmu, idx, CCI_PMU_CNTR); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci return value; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic void pmu_write_counter(struct cci_pmu *cci_pmu, u32 value, int idx) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci pmu_write_register(cci_pmu, value, idx, CCI_PMU_CNTR); 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic void __pmu_write_counters(struct cci_pmu *cci_pmu, unsigned long *mask) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci int i; 89662306a36Sopenharmony_ci struct cci_pmu_hw_events *cci_hw = &cci_pmu->hw_events; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci for_each_set_bit(i, mask, cci_pmu->num_cntrs) { 89962306a36Sopenharmony_ci struct perf_event *event = cci_hw->events[i]; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (WARN_ON(!event)) 90262306a36Sopenharmony_ci continue; 90362306a36Sopenharmony_ci pmu_write_counter(cci_pmu, local64_read(&event->hw.prev_count), i); 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic void pmu_write_counters(struct cci_pmu *cci_pmu, unsigned long *mask) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci if (cci_pmu->model->write_counters) 91062306a36Sopenharmony_ci cci_pmu->model->write_counters(cci_pmu, mask); 91162306a36Sopenharmony_ci else 91262306a36Sopenharmony_ci __pmu_write_counters(cci_pmu, mask); 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI5xx_PMU 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci/* 91862306a36Sopenharmony_ci * CCI-500/CCI-550 has advanced power saving policies, which could gate the 91962306a36Sopenharmony_ci * clocks to the PMU counters, which makes the writes to them ineffective. 92062306a36Sopenharmony_ci * The only way to write to those counters is when the global counters 92162306a36Sopenharmony_ci * are enabled and the particular counter is enabled. 92262306a36Sopenharmony_ci * 92362306a36Sopenharmony_ci * So we do the following : 92462306a36Sopenharmony_ci * 92562306a36Sopenharmony_ci * 1) Disable all the PMU counters, saving their current state 92662306a36Sopenharmony_ci * 2) Enable the global PMU profiling, now that all counters are 92762306a36Sopenharmony_ci * disabled. 92862306a36Sopenharmony_ci * 92962306a36Sopenharmony_ci * For each counter to be programmed, repeat steps 3-7: 93062306a36Sopenharmony_ci * 93162306a36Sopenharmony_ci * 3) Write an invalid event code to the event control register for the 93262306a36Sopenharmony_ci counter, so that the counters are not modified. 93362306a36Sopenharmony_ci * 4) Enable the counter control for the counter. 93462306a36Sopenharmony_ci * 5) Set the counter value 93562306a36Sopenharmony_ci * 6) Disable the counter 93662306a36Sopenharmony_ci * 7) Restore the event in the target counter 93762306a36Sopenharmony_ci * 93862306a36Sopenharmony_ci * 8) Disable the global PMU. 93962306a36Sopenharmony_ci * 9) Restore the status of the rest of the counters. 94062306a36Sopenharmony_ci * 94162306a36Sopenharmony_ci * We choose an event which for CCI-5xx is guaranteed not to count. 94262306a36Sopenharmony_ci * We use the highest possible event code (0x1f) for the master interface 0. 94362306a36Sopenharmony_ci */ 94462306a36Sopenharmony_ci#define CCI5xx_INVALID_EVENT ((CCI5xx_PORT_M0 << CCI5xx_PMU_EVENT_SOURCE_SHIFT) | \ 94562306a36Sopenharmony_ci (CCI5xx_PMU_EVENT_CODE_MASK << CCI5xx_PMU_EVENT_CODE_SHIFT)) 94662306a36Sopenharmony_cistatic void cci5xx_pmu_write_counters(struct cci_pmu *cci_pmu, unsigned long *mask) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci int i; 94962306a36Sopenharmony_ci DECLARE_BITMAP(saved_mask, HW_CNTRS_MAX); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci bitmap_zero(saved_mask, cci_pmu->num_cntrs); 95262306a36Sopenharmony_ci pmu_save_counters(cci_pmu, saved_mask); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci /* 95562306a36Sopenharmony_ci * Now that all the counters are disabled, we can safely turn the PMU on, 95662306a36Sopenharmony_ci * without syncing the status of the counters 95762306a36Sopenharmony_ci */ 95862306a36Sopenharmony_ci __cci_pmu_enable_nosync(cci_pmu); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci for_each_set_bit(i, mask, cci_pmu->num_cntrs) { 96162306a36Sopenharmony_ci struct perf_event *event = cci_pmu->hw_events.events[i]; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (WARN_ON(!event)) 96462306a36Sopenharmony_ci continue; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci pmu_set_event(cci_pmu, i, CCI5xx_INVALID_EVENT); 96762306a36Sopenharmony_ci pmu_enable_counter(cci_pmu, i); 96862306a36Sopenharmony_ci pmu_write_counter(cci_pmu, local64_read(&event->hw.prev_count), i); 96962306a36Sopenharmony_ci pmu_disable_counter(cci_pmu, i); 97062306a36Sopenharmony_ci pmu_set_event(cci_pmu, i, event->hw.config_base); 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci __cci_pmu_disable(cci_pmu); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci pmu_restore_counters(cci_pmu, saved_mask); 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci#endif /* CONFIG_ARM_CCI5xx_PMU */ 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic u64 pmu_event_update(struct perf_event *event) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 98362306a36Sopenharmony_ci u64 delta, prev_raw_count, new_raw_count; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci do { 98662306a36Sopenharmony_ci prev_raw_count = local64_read(&hwc->prev_count); 98762306a36Sopenharmony_ci new_raw_count = pmu_read_counter(event); 98862306a36Sopenharmony_ci } while (local64_cmpxchg(&hwc->prev_count, prev_raw_count, 98962306a36Sopenharmony_ci new_raw_count) != prev_raw_count); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci delta = (new_raw_count - prev_raw_count) & CCI_PMU_CNTR_MASK; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci local64_add(delta, &event->count); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return new_raw_count; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic void pmu_read(struct perf_event *event) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci pmu_event_update(event); 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic void pmu_event_set_period(struct perf_event *event) 100462306a36Sopenharmony_ci{ 100562306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 100662306a36Sopenharmony_ci /* 100762306a36Sopenharmony_ci * The CCI PMU counters have a period of 2^32. To account for the 100862306a36Sopenharmony_ci * possiblity of extreme interrupt latency we program for a period of 100962306a36Sopenharmony_ci * half that. Hopefully we can handle the interrupt before another 2^31 101062306a36Sopenharmony_ci * events occur and the counter overtakes its previous value. 101162306a36Sopenharmony_ci */ 101262306a36Sopenharmony_ci u64 val = 1ULL << 31; 101362306a36Sopenharmony_ci local64_set(&hwc->prev_count, val); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* 101662306a36Sopenharmony_ci * CCI PMU uses PERF_HES_ARCH to keep track of the counters, whose 101762306a36Sopenharmony_ci * values needs to be sync-ed with the s/w state before the PMU is 101862306a36Sopenharmony_ci * enabled. 101962306a36Sopenharmony_ci * Mark this counter for sync. 102062306a36Sopenharmony_ci */ 102162306a36Sopenharmony_ci hwc->state |= PERF_HES_ARCH; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cistatic irqreturn_t pmu_handle_irq(int irq_num, void *dev) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci struct cci_pmu *cci_pmu = dev; 102762306a36Sopenharmony_ci struct cci_pmu_hw_events *events = &cci_pmu->hw_events; 102862306a36Sopenharmony_ci int idx, handled = IRQ_NONE; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci raw_spin_lock(&events->pmu_lock); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci /* Disable the PMU while we walk through the counters */ 103362306a36Sopenharmony_ci __cci_pmu_disable(cci_pmu); 103462306a36Sopenharmony_ci /* 103562306a36Sopenharmony_ci * Iterate over counters and update the corresponding perf events. 103662306a36Sopenharmony_ci * This should work regardless of whether we have per-counter overflow 103762306a36Sopenharmony_ci * interrupt or a combined overflow interrupt. 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_ci for (idx = 0; idx <= CCI_PMU_CNTR_LAST(cci_pmu); idx++) { 104062306a36Sopenharmony_ci struct perf_event *event = events->events[idx]; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci if (!event) 104362306a36Sopenharmony_ci continue; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci /* Did this counter overflow? */ 104662306a36Sopenharmony_ci if (!(pmu_read_register(cci_pmu, idx, CCI_PMU_OVRFLW) & 104762306a36Sopenharmony_ci CCI_PMU_OVRFLW_FLAG)) 104862306a36Sopenharmony_ci continue; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci pmu_write_register(cci_pmu, CCI_PMU_OVRFLW_FLAG, idx, 105162306a36Sopenharmony_ci CCI_PMU_OVRFLW); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci pmu_event_update(event); 105462306a36Sopenharmony_ci pmu_event_set_period(event); 105562306a36Sopenharmony_ci handled = IRQ_HANDLED; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci /* Enable the PMU and sync possibly overflowed counters */ 105962306a36Sopenharmony_ci __cci_pmu_enable_sync(cci_pmu); 106062306a36Sopenharmony_ci raw_spin_unlock(&events->pmu_lock); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci return IRQ_RETVAL(handled); 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_cistatic int cci_pmu_get_hw(struct cci_pmu *cci_pmu) 106662306a36Sopenharmony_ci{ 106762306a36Sopenharmony_ci int ret = pmu_request_irq(cci_pmu, pmu_handle_irq); 106862306a36Sopenharmony_ci if (ret) { 106962306a36Sopenharmony_ci pmu_free_irq(cci_pmu); 107062306a36Sopenharmony_ci return ret; 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci return 0; 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic void cci_pmu_put_hw(struct cci_pmu *cci_pmu) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci pmu_free_irq(cci_pmu); 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic void hw_perf_event_destroy(struct perf_event *event) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); 108362306a36Sopenharmony_ci atomic_t *active_events = &cci_pmu->active_events; 108462306a36Sopenharmony_ci struct mutex *reserve_mutex = &cci_pmu->reserve_mutex; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci if (atomic_dec_and_mutex_lock(active_events, reserve_mutex)) { 108762306a36Sopenharmony_ci cci_pmu_put_hw(cci_pmu); 108862306a36Sopenharmony_ci mutex_unlock(reserve_mutex); 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_cistatic void cci_pmu_enable(struct pmu *pmu) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(pmu); 109562306a36Sopenharmony_ci struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; 109662306a36Sopenharmony_ci bool enabled = !bitmap_empty(hw_events->used_mask, cci_pmu->num_cntrs); 109762306a36Sopenharmony_ci unsigned long flags; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci if (!enabled) 110062306a36Sopenharmony_ci return; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci raw_spin_lock_irqsave(&hw_events->pmu_lock, flags); 110362306a36Sopenharmony_ci __cci_pmu_enable_sync(cci_pmu); 110462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_cistatic void cci_pmu_disable(struct pmu *pmu) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(pmu); 111162306a36Sopenharmony_ci struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; 111262306a36Sopenharmony_ci unsigned long flags; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci raw_spin_lock_irqsave(&hw_events->pmu_lock, flags); 111562306a36Sopenharmony_ci __cci_pmu_disable(cci_pmu); 111662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags); 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci/* 112062306a36Sopenharmony_ci * Check if the idx represents a non-programmable counter. 112162306a36Sopenharmony_ci * All the fixed event counters are mapped before the programmable 112262306a36Sopenharmony_ci * counters. 112362306a36Sopenharmony_ci */ 112462306a36Sopenharmony_cistatic bool pmu_fixed_hw_idx(struct cci_pmu *cci_pmu, int idx) 112562306a36Sopenharmony_ci{ 112662306a36Sopenharmony_ci return (idx >= 0) && (idx < cci_pmu->model->fixed_hw_cntrs); 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic void cci_pmu_start(struct perf_event *event, int pmu_flags) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); 113262306a36Sopenharmony_ci struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; 113362306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 113462306a36Sopenharmony_ci int idx = hwc->idx; 113562306a36Sopenharmony_ci unsigned long flags; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci /* 113862306a36Sopenharmony_ci * To handle interrupt latency, we always reprogram the period 113962306a36Sopenharmony_ci * regardless of PERF_EF_RELOAD. 114062306a36Sopenharmony_ci */ 114162306a36Sopenharmony_ci if (pmu_flags & PERF_EF_RELOAD) 114262306a36Sopenharmony_ci WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci hwc->state = 0; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { 114762306a36Sopenharmony_ci dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); 114862306a36Sopenharmony_ci return; 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci raw_spin_lock_irqsave(&hw_events->pmu_lock, flags); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci /* Configure the counter unless you are counting a fixed event */ 115462306a36Sopenharmony_ci if (!pmu_fixed_hw_idx(cci_pmu, idx)) 115562306a36Sopenharmony_ci pmu_set_event(cci_pmu, idx, hwc->config_base); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci pmu_event_set_period(event); 115862306a36Sopenharmony_ci pmu_enable_counter(cci_pmu, idx); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags); 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_cistatic void cci_pmu_stop(struct perf_event *event, int pmu_flags) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); 116662306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 116762306a36Sopenharmony_ci int idx = hwc->idx; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if (hwc->state & PERF_HES_STOPPED) 117062306a36Sopenharmony_ci return; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) { 117362306a36Sopenharmony_ci dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); 117462306a36Sopenharmony_ci return; 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci /* 117862306a36Sopenharmony_ci * We always reprogram the counter, so ignore PERF_EF_UPDATE. See 117962306a36Sopenharmony_ci * cci_pmu_start() 118062306a36Sopenharmony_ci */ 118162306a36Sopenharmony_ci pmu_disable_counter(cci_pmu, idx); 118262306a36Sopenharmony_ci pmu_event_update(event); 118362306a36Sopenharmony_ci hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; 118462306a36Sopenharmony_ci} 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistatic int cci_pmu_add(struct perf_event *event, int flags) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); 118962306a36Sopenharmony_ci struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; 119062306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 119162306a36Sopenharmony_ci int idx; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci /* If we don't have a space for the counter then finish early. */ 119462306a36Sopenharmony_ci idx = pmu_get_event_idx(hw_events, event); 119562306a36Sopenharmony_ci if (idx < 0) 119662306a36Sopenharmony_ci return idx; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci event->hw.idx = idx; 119962306a36Sopenharmony_ci hw_events->events[idx] = event; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; 120262306a36Sopenharmony_ci if (flags & PERF_EF_START) 120362306a36Sopenharmony_ci cci_pmu_start(event, PERF_EF_RELOAD); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* Propagate our changes to the userspace mapping. */ 120662306a36Sopenharmony_ci perf_event_update_userpage(event); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci return 0; 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic void cci_pmu_del(struct perf_event *event, int flags) 121262306a36Sopenharmony_ci{ 121362306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); 121462306a36Sopenharmony_ci struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events; 121562306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 121662306a36Sopenharmony_ci int idx = hwc->idx; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci cci_pmu_stop(event, PERF_EF_UPDATE); 121962306a36Sopenharmony_ci hw_events->events[idx] = NULL; 122062306a36Sopenharmony_ci clear_bit(idx, hw_events->used_mask); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci perf_event_update_userpage(event); 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_cistatic int validate_event(struct pmu *cci_pmu, 122662306a36Sopenharmony_ci struct cci_pmu_hw_events *hw_events, 122762306a36Sopenharmony_ci struct perf_event *event) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci if (is_software_event(event)) 123062306a36Sopenharmony_ci return 1; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci /* 123362306a36Sopenharmony_ci * Reject groups spanning multiple HW PMUs (e.g. CPU + CCI). The 123462306a36Sopenharmony_ci * core perf code won't check that the pmu->ctx == leader->ctx 123562306a36Sopenharmony_ci * until after pmu->event_init(event). 123662306a36Sopenharmony_ci */ 123762306a36Sopenharmony_ci if (event->pmu != cci_pmu) 123862306a36Sopenharmony_ci return 0; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci if (event->state < PERF_EVENT_STATE_OFF) 124162306a36Sopenharmony_ci return 1; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec) 124462306a36Sopenharmony_ci return 1; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci return pmu_get_event_idx(hw_events, event) >= 0; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_cistatic int validate_group(struct perf_event *event) 125062306a36Sopenharmony_ci{ 125162306a36Sopenharmony_ci struct perf_event *sibling, *leader = event->group_leader; 125262306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); 125362306a36Sopenharmony_ci unsigned long mask[BITS_TO_LONGS(HW_CNTRS_MAX)]; 125462306a36Sopenharmony_ci struct cci_pmu_hw_events fake_pmu = { 125562306a36Sopenharmony_ci /* 125662306a36Sopenharmony_ci * Initialise the fake PMU. We only need to populate the 125762306a36Sopenharmony_ci * used_mask for the purposes of validation. 125862306a36Sopenharmony_ci */ 125962306a36Sopenharmony_ci .used_mask = mask, 126062306a36Sopenharmony_ci }; 126162306a36Sopenharmony_ci bitmap_zero(mask, cci_pmu->num_cntrs); 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci if (!validate_event(event->pmu, &fake_pmu, leader)) 126462306a36Sopenharmony_ci return -EINVAL; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci for_each_sibling_event(sibling, leader) { 126762306a36Sopenharmony_ci if (!validate_event(event->pmu, &fake_pmu, sibling)) 126862306a36Sopenharmony_ci return -EINVAL; 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci if (!validate_event(event->pmu, &fake_pmu, event)) 127262306a36Sopenharmony_ci return -EINVAL; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci return 0; 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cistatic int __hw_perf_event_init(struct perf_event *event) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 128062306a36Sopenharmony_ci int mapping; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci mapping = pmu_map_event(event); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (mapping < 0) { 128562306a36Sopenharmony_ci pr_debug("event %x:%llx not supported\n", event->attr.type, 128662306a36Sopenharmony_ci event->attr.config); 128762306a36Sopenharmony_ci return mapping; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci /* 129162306a36Sopenharmony_ci * We don't assign an index until we actually place the event onto 129262306a36Sopenharmony_ci * hardware. Use -1 to signify that we haven't decided where to put it 129362306a36Sopenharmony_ci * yet. 129462306a36Sopenharmony_ci */ 129562306a36Sopenharmony_ci hwc->idx = -1; 129662306a36Sopenharmony_ci hwc->config_base = 0; 129762306a36Sopenharmony_ci hwc->config = 0; 129862306a36Sopenharmony_ci hwc->event_base = 0; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci /* 130162306a36Sopenharmony_ci * Store the event encoding into the config_base field. 130262306a36Sopenharmony_ci */ 130362306a36Sopenharmony_ci hwc->config_base |= (unsigned long)mapping; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci if (event->group_leader != event) { 130662306a36Sopenharmony_ci if (validate_group(event) != 0) 130762306a36Sopenharmony_ci return -EINVAL; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci return 0; 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_cistatic int cci_pmu_event_init(struct perf_event *event) 131462306a36Sopenharmony_ci{ 131562306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); 131662306a36Sopenharmony_ci atomic_t *active_events = &cci_pmu->active_events; 131762306a36Sopenharmony_ci int err = 0; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci if (event->attr.type != event->pmu->type) 132062306a36Sopenharmony_ci return -ENOENT; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci /* Shared by all CPUs, no meaningful state to sample */ 132362306a36Sopenharmony_ci if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) 132462306a36Sopenharmony_ci return -EOPNOTSUPP; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci /* 132762306a36Sopenharmony_ci * Following the example set by other "uncore" PMUs, we accept any CPU 132862306a36Sopenharmony_ci * and rewrite its affinity dynamically rather than having perf core 132962306a36Sopenharmony_ci * handle cpu == -1 and pid == -1 for this case. 133062306a36Sopenharmony_ci * 133162306a36Sopenharmony_ci * The perf core will pin online CPUs for the duration of this call and 133262306a36Sopenharmony_ci * the event being installed into its context, so the PMU's CPU can't 133362306a36Sopenharmony_ci * change under our feet. 133462306a36Sopenharmony_ci */ 133562306a36Sopenharmony_ci if (event->cpu < 0) 133662306a36Sopenharmony_ci return -EINVAL; 133762306a36Sopenharmony_ci event->cpu = cci_pmu->cpu; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci event->destroy = hw_perf_event_destroy; 134062306a36Sopenharmony_ci if (!atomic_inc_not_zero(active_events)) { 134162306a36Sopenharmony_ci mutex_lock(&cci_pmu->reserve_mutex); 134262306a36Sopenharmony_ci if (atomic_read(active_events) == 0) 134362306a36Sopenharmony_ci err = cci_pmu_get_hw(cci_pmu); 134462306a36Sopenharmony_ci if (!err) 134562306a36Sopenharmony_ci atomic_inc(active_events); 134662306a36Sopenharmony_ci mutex_unlock(&cci_pmu->reserve_mutex); 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci if (err) 134962306a36Sopenharmony_ci return err; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci err = __hw_perf_event_init(event); 135262306a36Sopenharmony_ci if (err) 135362306a36Sopenharmony_ci hw_perf_event_destroy(event); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci return err; 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic ssize_t pmu_cpumask_attr_show(struct device *dev, 135962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci struct pmu *pmu = dev_get_drvdata(dev); 136262306a36Sopenharmony_ci struct cci_pmu *cci_pmu = to_cci_pmu(pmu); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, cpumask_of(cci_pmu->cpu)); 136562306a36Sopenharmony_ci} 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_cistatic struct device_attribute pmu_cpumask_attr = 136862306a36Sopenharmony_ci __ATTR(cpumask, S_IRUGO, pmu_cpumask_attr_show, NULL); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_cistatic struct attribute *pmu_attrs[] = { 137162306a36Sopenharmony_ci &pmu_cpumask_attr.attr, 137262306a36Sopenharmony_ci NULL, 137362306a36Sopenharmony_ci}; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_cistatic const struct attribute_group pmu_attr_group = { 137662306a36Sopenharmony_ci .attrs = pmu_attrs, 137762306a36Sopenharmony_ci}; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_cistatic struct attribute_group pmu_format_attr_group = { 138062306a36Sopenharmony_ci .name = "format", 138162306a36Sopenharmony_ci .attrs = NULL, /* Filled in cci_pmu_init_attrs */ 138262306a36Sopenharmony_ci}; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic struct attribute_group pmu_event_attr_group = { 138562306a36Sopenharmony_ci .name = "events", 138662306a36Sopenharmony_ci .attrs = NULL, /* Filled in cci_pmu_init_attrs */ 138762306a36Sopenharmony_ci}; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_cistatic const struct attribute_group *pmu_attr_groups[] = { 139062306a36Sopenharmony_ci &pmu_attr_group, 139162306a36Sopenharmony_ci &pmu_format_attr_group, 139262306a36Sopenharmony_ci &pmu_event_attr_group, 139362306a36Sopenharmony_ci NULL 139462306a36Sopenharmony_ci}; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_cistatic int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci const struct cci_pmu_model *model = cci_pmu->model; 139962306a36Sopenharmony_ci char *name = model->name; 140062306a36Sopenharmony_ci u32 num_cntrs; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci if (WARN_ON(model->num_hw_cntrs > NUM_HW_CNTRS_MAX)) 140362306a36Sopenharmony_ci return -EINVAL; 140462306a36Sopenharmony_ci if (WARN_ON(model->fixed_hw_cntrs > FIXED_HW_CNTRS_MAX)) 140562306a36Sopenharmony_ci return -EINVAL; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci pmu_event_attr_group.attrs = model->event_attrs; 140862306a36Sopenharmony_ci pmu_format_attr_group.attrs = model->format_attrs; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci cci_pmu->pmu = (struct pmu) { 141162306a36Sopenharmony_ci .module = THIS_MODULE, 141262306a36Sopenharmony_ci .name = cci_pmu->model->name, 141362306a36Sopenharmony_ci .task_ctx_nr = perf_invalid_context, 141462306a36Sopenharmony_ci .pmu_enable = cci_pmu_enable, 141562306a36Sopenharmony_ci .pmu_disable = cci_pmu_disable, 141662306a36Sopenharmony_ci .event_init = cci_pmu_event_init, 141762306a36Sopenharmony_ci .add = cci_pmu_add, 141862306a36Sopenharmony_ci .del = cci_pmu_del, 141962306a36Sopenharmony_ci .start = cci_pmu_start, 142062306a36Sopenharmony_ci .stop = cci_pmu_stop, 142162306a36Sopenharmony_ci .read = pmu_read, 142262306a36Sopenharmony_ci .attr_groups = pmu_attr_groups, 142362306a36Sopenharmony_ci .capabilities = PERF_PMU_CAP_NO_EXCLUDE, 142462306a36Sopenharmony_ci }; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci cci_pmu->plat_device = pdev; 142762306a36Sopenharmony_ci num_cntrs = pmu_get_max_counters(cci_pmu); 142862306a36Sopenharmony_ci if (num_cntrs > cci_pmu->model->num_hw_cntrs) { 142962306a36Sopenharmony_ci dev_warn(&pdev->dev, 143062306a36Sopenharmony_ci "PMU implements more counters(%d) than supported by" 143162306a36Sopenharmony_ci " the model(%d), truncated.", 143262306a36Sopenharmony_ci num_cntrs, cci_pmu->model->num_hw_cntrs); 143362306a36Sopenharmony_ci num_cntrs = cci_pmu->model->num_hw_cntrs; 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci cci_pmu->num_cntrs = num_cntrs + cci_pmu->model->fixed_hw_cntrs; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci return perf_pmu_register(&cci_pmu->pmu, name, -1); 143862306a36Sopenharmony_ci} 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_cistatic int cci_pmu_offline_cpu(unsigned int cpu) 144162306a36Sopenharmony_ci{ 144262306a36Sopenharmony_ci int target; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci if (!g_cci_pmu || cpu != g_cci_pmu->cpu) 144562306a36Sopenharmony_ci return 0; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci target = cpumask_any_but(cpu_online_mask, cpu); 144862306a36Sopenharmony_ci if (target >= nr_cpu_ids) 144962306a36Sopenharmony_ci return 0; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci perf_pmu_migrate_context(&g_cci_pmu->pmu, cpu, target); 145262306a36Sopenharmony_ci g_cci_pmu->cpu = target; 145362306a36Sopenharmony_ci return 0; 145462306a36Sopenharmony_ci} 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_cistatic __maybe_unused struct cci_pmu_model cci_pmu_models[] = { 145762306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI400_PMU 145862306a36Sopenharmony_ci [CCI400_R0] = { 145962306a36Sopenharmony_ci .name = "CCI_400", 146062306a36Sopenharmony_ci .fixed_hw_cntrs = FIXED_HW_CNTRS_CII_4XX, /* Cycle counter */ 146162306a36Sopenharmony_ci .num_hw_cntrs = NUM_HW_CNTRS_CII_4XX, 146262306a36Sopenharmony_ci .cntr_size = SZ_4K, 146362306a36Sopenharmony_ci .format_attrs = cci400_pmu_format_attrs, 146462306a36Sopenharmony_ci .event_attrs = cci400_r0_pmu_event_attrs, 146562306a36Sopenharmony_ci .event_ranges = { 146662306a36Sopenharmony_ci [CCI_IF_SLAVE] = { 146762306a36Sopenharmony_ci CCI400_R0_SLAVE_PORT_MIN_EV, 146862306a36Sopenharmony_ci CCI400_R0_SLAVE_PORT_MAX_EV, 146962306a36Sopenharmony_ci }, 147062306a36Sopenharmony_ci [CCI_IF_MASTER] = { 147162306a36Sopenharmony_ci CCI400_R0_MASTER_PORT_MIN_EV, 147262306a36Sopenharmony_ci CCI400_R0_MASTER_PORT_MAX_EV, 147362306a36Sopenharmony_ci }, 147462306a36Sopenharmony_ci }, 147562306a36Sopenharmony_ci .validate_hw_event = cci400_validate_hw_event, 147662306a36Sopenharmony_ci .get_event_idx = cci400_get_event_idx, 147762306a36Sopenharmony_ci }, 147862306a36Sopenharmony_ci [CCI400_R1] = { 147962306a36Sopenharmony_ci .name = "CCI_400_r1", 148062306a36Sopenharmony_ci .fixed_hw_cntrs = FIXED_HW_CNTRS_CII_4XX, /* Cycle counter */ 148162306a36Sopenharmony_ci .num_hw_cntrs = NUM_HW_CNTRS_CII_4XX, 148262306a36Sopenharmony_ci .cntr_size = SZ_4K, 148362306a36Sopenharmony_ci .format_attrs = cci400_pmu_format_attrs, 148462306a36Sopenharmony_ci .event_attrs = cci400_r1_pmu_event_attrs, 148562306a36Sopenharmony_ci .event_ranges = { 148662306a36Sopenharmony_ci [CCI_IF_SLAVE] = { 148762306a36Sopenharmony_ci CCI400_R1_SLAVE_PORT_MIN_EV, 148862306a36Sopenharmony_ci CCI400_R1_SLAVE_PORT_MAX_EV, 148962306a36Sopenharmony_ci }, 149062306a36Sopenharmony_ci [CCI_IF_MASTER] = { 149162306a36Sopenharmony_ci CCI400_R1_MASTER_PORT_MIN_EV, 149262306a36Sopenharmony_ci CCI400_R1_MASTER_PORT_MAX_EV, 149362306a36Sopenharmony_ci }, 149462306a36Sopenharmony_ci }, 149562306a36Sopenharmony_ci .validate_hw_event = cci400_validate_hw_event, 149662306a36Sopenharmony_ci .get_event_idx = cci400_get_event_idx, 149762306a36Sopenharmony_ci }, 149862306a36Sopenharmony_ci#endif 149962306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI5xx_PMU 150062306a36Sopenharmony_ci [CCI500_R0] = { 150162306a36Sopenharmony_ci .name = "CCI_500", 150262306a36Sopenharmony_ci .fixed_hw_cntrs = FIXED_HW_CNTRS_CII_5XX, 150362306a36Sopenharmony_ci .num_hw_cntrs = NUM_HW_CNTRS_CII_5XX, 150462306a36Sopenharmony_ci .cntr_size = SZ_64K, 150562306a36Sopenharmony_ci .format_attrs = cci5xx_pmu_format_attrs, 150662306a36Sopenharmony_ci .event_attrs = cci5xx_pmu_event_attrs, 150762306a36Sopenharmony_ci .event_ranges = { 150862306a36Sopenharmony_ci [CCI_IF_SLAVE] = { 150962306a36Sopenharmony_ci CCI5xx_SLAVE_PORT_MIN_EV, 151062306a36Sopenharmony_ci CCI5xx_SLAVE_PORT_MAX_EV, 151162306a36Sopenharmony_ci }, 151262306a36Sopenharmony_ci [CCI_IF_MASTER] = { 151362306a36Sopenharmony_ci CCI5xx_MASTER_PORT_MIN_EV, 151462306a36Sopenharmony_ci CCI5xx_MASTER_PORT_MAX_EV, 151562306a36Sopenharmony_ci }, 151662306a36Sopenharmony_ci [CCI_IF_GLOBAL] = { 151762306a36Sopenharmony_ci CCI5xx_GLOBAL_PORT_MIN_EV, 151862306a36Sopenharmony_ci CCI5xx_GLOBAL_PORT_MAX_EV, 151962306a36Sopenharmony_ci }, 152062306a36Sopenharmony_ci }, 152162306a36Sopenharmony_ci .validate_hw_event = cci500_validate_hw_event, 152262306a36Sopenharmony_ci .write_counters = cci5xx_pmu_write_counters, 152362306a36Sopenharmony_ci }, 152462306a36Sopenharmony_ci [CCI550_R0] = { 152562306a36Sopenharmony_ci .name = "CCI_550", 152662306a36Sopenharmony_ci .fixed_hw_cntrs = FIXED_HW_CNTRS_CII_5XX, 152762306a36Sopenharmony_ci .num_hw_cntrs = NUM_HW_CNTRS_CII_5XX, 152862306a36Sopenharmony_ci .cntr_size = SZ_64K, 152962306a36Sopenharmony_ci .format_attrs = cci5xx_pmu_format_attrs, 153062306a36Sopenharmony_ci .event_attrs = cci5xx_pmu_event_attrs, 153162306a36Sopenharmony_ci .event_ranges = { 153262306a36Sopenharmony_ci [CCI_IF_SLAVE] = { 153362306a36Sopenharmony_ci CCI5xx_SLAVE_PORT_MIN_EV, 153462306a36Sopenharmony_ci CCI5xx_SLAVE_PORT_MAX_EV, 153562306a36Sopenharmony_ci }, 153662306a36Sopenharmony_ci [CCI_IF_MASTER] = { 153762306a36Sopenharmony_ci CCI5xx_MASTER_PORT_MIN_EV, 153862306a36Sopenharmony_ci CCI5xx_MASTER_PORT_MAX_EV, 153962306a36Sopenharmony_ci }, 154062306a36Sopenharmony_ci [CCI_IF_GLOBAL] = { 154162306a36Sopenharmony_ci CCI5xx_GLOBAL_PORT_MIN_EV, 154262306a36Sopenharmony_ci CCI5xx_GLOBAL_PORT_MAX_EV, 154362306a36Sopenharmony_ci }, 154462306a36Sopenharmony_ci }, 154562306a36Sopenharmony_ci .validate_hw_event = cci550_validate_hw_event, 154662306a36Sopenharmony_ci .write_counters = cci5xx_pmu_write_counters, 154762306a36Sopenharmony_ci }, 154862306a36Sopenharmony_ci#endif 154962306a36Sopenharmony_ci}; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_cistatic const struct of_device_id arm_cci_pmu_matches[] = { 155262306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI400_PMU 155362306a36Sopenharmony_ci { 155462306a36Sopenharmony_ci .compatible = "arm,cci-400-pmu", 155562306a36Sopenharmony_ci .data = NULL, 155662306a36Sopenharmony_ci }, 155762306a36Sopenharmony_ci { 155862306a36Sopenharmony_ci .compatible = "arm,cci-400-pmu,r0", 155962306a36Sopenharmony_ci .data = &cci_pmu_models[CCI400_R0], 156062306a36Sopenharmony_ci }, 156162306a36Sopenharmony_ci { 156262306a36Sopenharmony_ci .compatible = "arm,cci-400-pmu,r1", 156362306a36Sopenharmony_ci .data = &cci_pmu_models[CCI400_R1], 156462306a36Sopenharmony_ci }, 156562306a36Sopenharmony_ci#endif 156662306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI5xx_PMU 156762306a36Sopenharmony_ci { 156862306a36Sopenharmony_ci .compatible = "arm,cci-500-pmu,r0", 156962306a36Sopenharmony_ci .data = &cci_pmu_models[CCI500_R0], 157062306a36Sopenharmony_ci }, 157162306a36Sopenharmony_ci { 157262306a36Sopenharmony_ci .compatible = "arm,cci-550-pmu,r0", 157362306a36Sopenharmony_ci .data = &cci_pmu_models[CCI550_R0], 157462306a36Sopenharmony_ci }, 157562306a36Sopenharmony_ci#endif 157662306a36Sopenharmony_ci {}, 157762306a36Sopenharmony_ci}; 157862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, arm_cci_pmu_matches); 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_cistatic bool is_duplicate_irq(int irq, int *irqs, int nr_irqs) 158162306a36Sopenharmony_ci{ 158262306a36Sopenharmony_ci int i; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci for (i = 0; i < nr_irqs; i++) 158562306a36Sopenharmony_ci if (irq == irqs[i]) 158662306a36Sopenharmony_ci return true; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci return false; 158962306a36Sopenharmony_ci} 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_cistatic struct cci_pmu *cci_pmu_alloc(struct device *dev) 159262306a36Sopenharmony_ci{ 159362306a36Sopenharmony_ci struct cci_pmu *cci_pmu; 159462306a36Sopenharmony_ci const struct cci_pmu_model *model; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci /* 159762306a36Sopenharmony_ci * All allocations are devm_* hence we don't have to free 159862306a36Sopenharmony_ci * them explicitly on an error, as it would end up in driver 159962306a36Sopenharmony_ci * detach. 160062306a36Sopenharmony_ci */ 160162306a36Sopenharmony_ci cci_pmu = devm_kzalloc(dev, sizeof(*cci_pmu), GFP_KERNEL); 160262306a36Sopenharmony_ci if (!cci_pmu) 160362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci cci_pmu->ctrl_base = *(void __iomem **)dev->platform_data; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci model = of_device_get_match_data(dev); 160862306a36Sopenharmony_ci if (!model) { 160962306a36Sopenharmony_ci dev_warn(dev, 161062306a36Sopenharmony_ci "DEPRECATED compatible property, requires secure access to CCI registers"); 161162306a36Sopenharmony_ci model = probe_cci_model(cci_pmu); 161262306a36Sopenharmony_ci } 161362306a36Sopenharmony_ci if (!model) { 161462306a36Sopenharmony_ci dev_warn(dev, "CCI PMU version not supported\n"); 161562306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci cci_pmu->model = model; 161962306a36Sopenharmony_ci cci_pmu->irqs = devm_kcalloc(dev, CCI_PMU_MAX_HW_CNTRS(model), 162062306a36Sopenharmony_ci sizeof(*cci_pmu->irqs), GFP_KERNEL); 162162306a36Sopenharmony_ci if (!cci_pmu->irqs) 162262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 162362306a36Sopenharmony_ci cci_pmu->hw_events.events = devm_kcalloc(dev, 162462306a36Sopenharmony_ci CCI_PMU_MAX_HW_CNTRS(model), 162562306a36Sopenharmony_ci sizeof(*cci_pmu->hw_events.events), 162662306a36Sopenharmony_ci GFP_KERNEL); 162762306a36Sopenharmony_ci if (!cci_pmu->hw_events.events) 162862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 162962306a36Sopenharmony_ci cci_pmu->hw_events.used_mask = devm_bitmap_zalloc(dev, 163062306a36Sopenharmony_ci CCI_PMU_MAX_HW_CNTRS(model), 163162306a36Sopenharmony_ci GFP_KERNEL); 163262306a36Sopenharmony_ci if (!cci_pmu->hw_events.used_mask) 163362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci return cci_pmu; 163662306a36Sopenharmony_ci} 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_cistatic int cci_pmu_probe(struct platform_device *pdev) 163962306a36Sopenharmony_ci{ 164062306a36Sopenharmony_ci struct cci_pmu *cci_pmu; 164162306a36Sopenharmony_ci int i, ret, irq; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci cci_pmu = cci_pmu_alloc(&pdev->dev); 164462306a36Sopenharmony_ci if (IS_ERR(cci_pmu)) 164562306a36Sopenharmony_ci return PTR_ERR(cci_pmu); 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci cci_pmu->base = devm_platform_ioremap_resource(pdev, 0); 164862306a36Sopenharmony_ci if (IS_ERR(cci_pmu->base)) 164962306a36Sopenharmony_ci return -ENOMEM; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci /* 165262306a36Sopenharmony_ci * CCI PMU has one overflow interrupt per counter; but some may be tied 165362306a36Sopenharmony_ci * together to a common interrupt. 165462306a36Sopenharmony_ci */ 165562306a36Sopenharmony_ci cci_pmu->nr_irqs = 0; 165662306a36Sopenharmony_ci for (i = 0; i < CCI_PMU_MAX_HW_CNTRS(cci_pmu->model); i++) { 165762306a36Sopenharmony_ci irq = platform_get_irq(pdev, i); 165862306a36Sopenharmony_ci if (irq < 0) 165962306a36Sopenharmony_ci break; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci if (is_duplicate_irq(irq, cci_pmu->irqs, cci_pmu->nr_irqs)) 166262306a36Sopenharmony_ci continue; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci cci_pmu->irqs[cci_pmu->nr_irqs++] = irq; 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci /* 166862306a36Sopenharmony_ci * Ensure that the device tree has as many interrupts as the number 166962306a36Sopenharmony_ci * of counters. 167062306a36Sopenharmony_ci */ 167162306a36Sopenharmony_ci if (i < CCI_PMU_MAX_HW_CNTRS(cci_pmu->model)) { 167262306a36Sopenharmony_ci dev_warn(&pdev->dev, "In-correct number of interrupts: %d, should be %d\n", 167362306a36Sopenharmony_ci i, CCI_PMU_MAX_HW_CNTRS(cci_pmu->model)); 167462306a36Sopenharmony_ci return -EINVAL; 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci raw_spin_lock_init(&cci_pmu->hw_events.pmu_lock); 167862306a36Sopenharmony_ci mutex_init(&cci_pmu->reserve_mutex); 167962306a36Sopenharmony_ci atomic_set(&cci_pmu->active_events, 0); 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci cci_pmu->cpu = raw_smp_processor_id(); 168262306a36Sopenharmony_ci g_cci_pmu = cci_pmu; 168362306a36Sopenharmony_ci cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_CCI_ONLINE, 168462306a36Sopenharmony_ci "perf/arm/cci:online", NULL, 168562306a36Sopenharmony_ci cci_pmu_offline_cpu); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci ret = cci_pmu_init(cci_pmu, pdev); 168862306a36Sopenharmony_ci if (ret) 168962306a36Sopenharmony_ci goto error_pmu_init; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci pr_info("ARM %s PMU driver probed", cci_pmu->model->name); 169262306a36Sopenharmony_ci return 0; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_cierror_pmu_init: 169562306a36Sopenharmony_ci cpuhp_remove_state(CPUHP_AP_PERF_ARM_CCI_ONLINE); 169662306a36Sopenharmony_ci g_cci_pmu = NULL; 169762306a36Sopenharmony_ci return ret; 169862306a36Sopenharmony_ci} 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_cistatic int cci_pmu_remove(struct platform_device *pdev) 170162306a36Sopenharmony_ci{ 170262306a36Sopenharmony_ci if (!g_cci_pmu) 170362306a36Sopenharmony_ci return 0; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci cpuhp_remove_state(CPUHP_AP_PERF_ARM_CCI_ONLINE); 170662306a36Sopenharmony_ci perf_pmu_unregister(&g_cci_pmu->pmu); 170762306a36Sopenharmony_ci g_cci_pmu = NULL; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci return 0; 171062306a36Sopenharmony_ci} 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_cistatic struct platform_driver cci_pmu_driver = { 171362306a36Sopenharmony_ci .driver = { 171462306a36Sopenharmony_ci .name = DRIVER_NAME, 171562306a36Sopenharmony_ci .of_match_table = arm_cci_pmu_matches, 171662306a36Sopenharmony_ci .suppress_bind_attrs = true, 171762306a36Sopenharmony_ci }, 171862306a36Sopenharmony_ci .probe = cci_pmu_probe, 171962306a36Sopenharmony_ci .remove = cci_pmu_remove, 172062306a36Sopenharmony_ci}; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_cimodule_platform_driver(cci_pmu_driver); 172362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 172462306a36Sopenharmony_ciMODULE_DESCRIPTION("ARM CCI PMU support"); 1725