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