162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CAVIUM THUNDERX2 SoC PMU UNCORE
462306a36Sopenharmony_ci * Copyright (C) 2018 Cavium Inc.
562306a36Sopenharmony_ci * Author: Ganapatrao Kulkarni <gkulkarni@cavium.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/acpi.h>
962306a36Sopenharmony_ci#include <linux/cpuhotplug.h>
1062306a36Sopenharmony_ci#include <linux/perf_event.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/* Each ThunderX2(TX2) Socket has a L3C and DMC UNCORE PMU device.
1462306a36Sopenharmony_ci * Each UNCORE PMU device consists of 4 independent programmable counters.
1562306a36Sopenharmony_ci * Counters are 32 bit and do not support overflow interrupt,
1662306a36Sopenharmony_ci * they need to be sampled before overflow(i.e, at every 2 seconds).
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define TX2_PMU_DMC_L3C_MAX_COUNTERS	4
2062306a36Sopenharmony_ci#define TX2_PMU_CCPI2_MAX_COUNTERS	8
2162306a36Sopenharmony_ci#define TX2_PMU_MAX_COUNTERS		TX2_PMU_CCPI2_MAX_COUNTERS
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define TX2_PMU_DMC_CHANNELS		8
2562306a36Sopenharmony_ci#define TX2_PMU_L3_TILES		16
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define TX2_PMU_HRTIMER_INTERVAL	(2 * NSEC_PER_SEC)
2862306a36Sopenharmony_ci#define GET_EVENTID(ev, mask)		((ev->hw.config) & mask)
2962306a36Sopenharmony_ci#define GET_COUNTERID(ev, mask)		((ev->hw.idx) & mask)
3062306a36Sopenharmony_ci /* 1 byte per counter(4 counters).
3162306a36Sopenharmony_ci  * Event id is encoded in bits [5:1] of a byte,
3262306a36Sopenharmony_ci  */
3362306a36Sopenharmony_ci#define DMC_EVENT_CFG(idx, val)		((val) << (((idx) * 8) + 1))
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* bits[3:0] to select counters, are indexed from 8 to 15. */
3662306a36Sopenharmony_ci#define CCPI2_COUNTER_OFFSET		8
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define L3C_COUNTER_CTL			0xA8
3962306a36Sopenharmony_ci#define L3C_COUNTER_DATA		0xAC
4062306a36Sopenharmony_ci#define DMC_COUNTER_CTL			0x234
4162306a36Sopenharmony_ci#define DMC_COUNTER_DATA		0x240
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define CCPI2_PERF_CTL			0x108
4462306a36Sopenharmony_ci#define CCPI2_COUNTER_CTL		0x10C
4562306a36Sopenharmony_ci#define CCPI2_COUNTER_SEL		0x12c
4662306a36Sopenharmony_ci#define CCPI2_COUNTER_DATA_L		0x130
4762306a36Sopenharmony_ci#define CCPI2_COUNTER_DATA_H		0x134
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* L3C event IDs */
5062306a36Sopenharmony_ci#define L3_EVENT_READ_REQ		0xD
5162306a36Sopenharmony_ci#define L3_EVENT_WRITEBACK_REQ		0xE
5262306a36Sopenharmony_ci#define L3_EVENT_INV_N_WRITE_REQ	0xF
5362306a36Sopenharmony_ci#define L3_EVENT_INV_REQ		0x10
5462306a36Sopenharmony_ci#define L3_EVENT_EVICT_REQ		0x13
5562306a36Sopenharmony_ci#define L3_EVENT_INV_N_WRITE_HIT	0x14
5662306a36Sopenharmony_ci#define L3_EVENT_INV_HIT		0x15
5762306a36Sopenharmony_ci#define L3_EVENT_READ_HIT		0x17
5862306a36Sopenharmony_ci#define L3_EVENT_MAX			0x18
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* DMC event IDs */
6162306a36Sopenharmony_ci#define DMC_EVENT_COUNT_CYCLES		0x1
6262306a36Sopenharmony_ci#define DMC_EVENT_WRITE_TXNS		0xB
6362306a36Sopenharmony_ci#define DMC_EVENT_DATA_TRANSFERS	0xD
6462306a36Sopenharmony_ci#define DMC_EVENT_READ_TXNS		0xF
6562306a36Sopenharmony_ci#define DMC_EVENT_MAX			0x10
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define CCPI2_EVENT_REQ_PKT_SENT	0x3D
6862306a36Sopenharmony_ci#define CCPI2_EVENT_SNOOP_PKT_SENT	0x65
6962306a36Sopenharmony_ci#define CCPI2_EVENT_DATA_PKT_SENT	0x105
7062306a36Sopenharmony_ci#define CCPI2_EVENT_GIC_PKT_SENT	0x12D
7162306a36Sopenharmony_ci#define CCPI2_EVENT_MAX			0x200
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define CCPI2_PERF_CTL_ENABLE		BIT(0)
7462306a36Sopenharmony_ci#define CCPI2_PERF_CTL_START		BIT(1)
7562306a36Sopenharmony_ci#define CCPI2_PERF_CTL_RESET		BIT(4)
7662306a36Sopenharmony_ci#define CCPI2_EVENT_LEVEL_RISING_EDGE	BIT(10)
7762306a36Sopenharmony_ci#define CCPI2_EVENT_TYPE_EDGE_SENSITIVE	BIT(11)
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cienum tx2_uncore_type {
8062306a36Sopenharmony_ci	PMU_TYPE_L3C,
8162306a36Sopenharmony_ci	PMU_TYPE_DMC,
8262306a36Sopenharmony_ci	PMU_TYPE_CCPI2,
8362306a36Sopenharmony_ci	PMU_TYPE_INVALID,
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/*
8762306a36Sopenharmony_ci * Each socket has 3 uncore devices associated with a PMU. The DMC and
8862306a36Sopenharmony_ci * L3C have 4 32-bit counters and the CCPI2 has 8 64-bit counters.
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistruct tx2_uncore_pmu {
9162306a36Sopenharmony_ci	struct hlist_node hpnode;
9262306a36Sopenharmony_ci	struct list_head  entry;
9362306a36Sopenharmony_ci	struct pmu pmu;
9462306a36Sopenharmony_ci	char *name;
9562306a36Sopenharmony_ci	int node;
9662306a36Sopenharmony_ci	int cpu;
9762306a36Sopenharmony_ci	u32 max_counters;
9862306a36Sopenharmony_ci	u32 counters_mask;
9962306a36Sopenharmony_ci	u32 prorate_factor;
10062306a36Sopenharmony_ci	u32 max_events;
10162306a36Sopenharmony_ci	u32 events_mask;
10262306a36Sopenharmony_ci	u64 hrtimer_interval;
10362306a36Sopenharmony_ci	void __iomem *base;
10462306a36Sopenharmony_ci	DECLARE_BITMAP(active_counters, TX2_PMU_MAX_COUNTERS);
10562306a36Sopenharmony_ci	struct perf_event *events[TX2_PMU_MAX_COUNTERS];
10662306a36Sopenharmony_ci	struct device *dev;
10762306a36Sopenharmony_ci	struct hrtimer hrtimer;
10862306a36Sopenharmony_ci	const struct attribute_group **attr_groups;
10962306a36Sopenharmony_ci	enum tx2_uncore_type type;
11062306a36Sopenharmony_ci	enum hrtimer_restart (*hrtimer_callback)(struct hrtimer *cb);
11162306a36Sopenharmony_ci	void (*init_cntr_base)(struct perf_event *event,
11262306a36Sopenharmony_ci			struct tx2_uncore_pmu *tx2_pmu);
11362306a36Sopenharmony_ci	void (*stop_event)(struct perf_event *event);
11462306a36Sopenharmony_ci	void (*start_event)(struct perf_event *event, int flags);
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic LIST_HEAD(tx2_pmus);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic inline struct tx2_uncore_pmu *pmu_to_tx2_pmu(struct pmu *pmu)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	return container_of(pmu, struct tx2_uncore_pmu, pmu);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define TX2_PMU_FORMAT_ATTR(_var, _name, _format)			\
12562306a36Sopenharmony_cistatic ssize_t								\
12662306a36Sopenharmony_ci__tx2_pmu_##_var##_show(struct device *dev,				\
12762306a36Sopenharmony_ci			       struct device_attribute *attr,		\
12862306a36Sopenharmony_ci			       char *page)				\
12962306a36Sopenharmony_ci{									\
13062306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE);			\
13162306a36Sopenharmony_ci	return sysfs_emit(page, _format "\n");				\
13262306a36Sopenharmony_ci}									\
13362306a36Sopenharmony_ci									\
13462306a36Sopenharmony_cistatic struct device_attribute format_attr_##_var =			\
13562306a36Sopenharmony_ci	__ATTR(_name, 0444, __tx2_pmu_##_var##_show, NULL)
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciTX2_PMU_FORMAT_ATTR(event, event, "config:0-4");
13862306a36Sopenharmony_ciTX2_PMU_FORMAT_ATTR(event_ccpi2, event, "config:0-9");
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic struct attribute *l3c_pmu_format_attrs[] = {
14162306a36Sopenharmony_ci	&format_attr_event.attr,
14262306a36Sopenharmony_ci	NULL,
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic struct attribute *dmc_pmu_format_attrs[] = {
14662306a36Sopenharmony_ci	&format_attr_event.attr,
14762306a36Sopenharmony_ci	NULL,
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic struct attribute *ccpi2_pmu_format_attrs[] = {
15162306a36Sopenharmony_ci	&format_attr_event_ccpi2.attr,
15262306a36Sopenharmony_ci	NULL,
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic const struct attribute_group l3c_pmu_format_attr_group = {
15662306a36Sopenharmony_ci	.name = "format",
15762306a36Sopenharmony_ci	.attrs = l3c_pmu_format_attrs,
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic const struct attribute_group dmc_pmu_format_attr_group = {
16162306a36Sopenharmony_ci	.name = "format",
16262306a36Sopenharmony_ci	.attrs = dmc_pmu_format_attrs,
16362306a36Sopenharmony_ci};
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic const struct attribute_group ccpi2_pmu_format_attr_group = {
16662306a36Sopenharmony_ci	.name = "format",
16762306a36Sopenharmony_ci	.attrs = ccpi2_pmu_format_attrs,
16862306a36Sopenharmony_ci};
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/*
17162306a36Sopenharmony_ci * sysfs event attributes
17262306a36Sopenharmony_ci */
17362306a36Sopenharmony_cistatic ssize_t tx2_pmu_event_show(struct device *dev,
17462306a36Sopenharmony_ci				    struct device_attribute *attr, char *buf)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct dev_ext_attribute *eattr;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	eattr = container_of(attr, struct dev_ext_attribute, attr);
17962306a36Sopenharmony_ci	return sysfs_emit(buf, "event=0x%lx\n", (unsigned long) eattr->var);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci#define TX2_EVENT_ATTR(name, config) \
18362306a36Sopenharmony_ci	PMU_EVENT_ATTR(name, tx2_pmu_event_attr_##name, \
18462306a36Sopenharmony_ci			config, tx2_pmu_event_show)
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ciTX2_EVENT_ATTR(read_request, L3_EVENT_READ_REQ);
18762306a36Sopenharmony_ciTX2_EVENT_ATTR(writeback_request, L3_EVENT_WRITEBACK_REQ);
18862306a36Sopenharmony_ciTX2_EVENT_ATTR(inv_nwrite_request, L3_EVENT_INV_N_WRITE_REQ);
18962306a36Sopenharmony_ciTX2_EVENT_ATTR(inv_request, L3_EVENT_INV_REQ);
19062306a36Sopenharmony_ciTX2_EVENT_ATTR(evict_request, L3_EVENT_EVICT_REQ);
19162306a36Sopenharmony_ciTX2_EVENT_ATTR(inv_nwrite_hit, L3_EVENT_INV_N_WRITE_HIT);
19262306a36Sopenharmony_ciTX2_EVENT_ATTR(inv_hit, L3_EVENT_INV_HIT);
19362306a36Sopenharmony_ciTX2_EVENT_ATTR(read_hit, L3_EVENT_READ_HIT);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic struct attribute *l3c_pmu_events_attrs[] = {
19662306a36Sopenharmony_ci	&tx2_pmu_event_attr_read_request.attr.attr,
19762306a36Sopenharmony_ci	&tx2_pmu_event_attr_writeback_request.attr.attr,
19862306a36Sopenharmony_ci	&tx2_pmu_event_attr_inv_nwrite_request.attr.attr,
19962306a36Sopenharmony_ci	&tx2_pmu_event_attr_inv_request.attr.attr,
20062306a36Sopenharmony_ci	&tx2_pmu_event_attr_evict_request.attr.attr,
20162306a36Sopenharmony_ci	&tx2_pmu_event_attr_inv_nwrite_hit.attr.attr,
20262306a36Sopenharmony_ci	&tx2_pmu_event_attr_inv_hit.attr.attr,
20362306a36Sopenharmony_ci	&tx2_pmu_event_attr_read_hit.attr.attr,
20462306a36Sopenharmony_ci	NULL,
20562306a36Sopenharmony_ci};
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ciTX2_EVENT_ATTR(cnt_cycles, DMC_EVENT_COUNT_CYCLES);
20862306a36Sopenharmony_ciTX2_EVENT_ATTR(write_txns, DMC_EVENT_WRITE_TXNS);
20962306a36Sopenharmony_ciTX2_EVENT_ATTR(data_transfers, DMC_EVENT_DATA_TRANSFERS);
21062306a36Sopenharmony_ciTX2_EVENT_ATTR(read_txns, DMC_EVENT_READ_TXNS);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic struct attribute *dmc_pmu_events_attrs[] = {
21362306a36Sopenharmony_ci	&tx2_pmu_event_attr_cnt_cycles.attr.attr,
21462306a36Sopenharmony_ci	&tx2_pmu_event_attr_write_txns.attr.attr,
21562306a36Sopenharmony_ci	&tx2_pmu_event_attr_data_transfers.attr.attr,
21662306a36Sopenharmony_ci	&tx2_pmu_event_attr_read_txns.attr.attr,
21762306a36Sopenharmony_ci	NULL,
21862306a36Sopenharmony_ci};
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ciTX2_EVENT_ATTR(req_pktsent, CCPI2_EVENT_REQ_PKT_SENT);
22162306a36Sopenharmony_ciTX2_EVENT_ATTR(snoop_pktsent, CCPI2_EVENT_SNOOP_PKT_SENT);
22262306a36Sopenharmony_ciTX2_EVENT_ATTR(data_pktsent, CCPI2_EVENT_DATA_PKT_SENT);
22362306a36Sopenharmony_ciTX2_EVENT_ATTR(gic_pktsent, CCPI2_EVENT_GIC_PKT_SENT);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic struct attribute *ccpi2_pmu_events_attrs[] = {
22662306a36Sopenharmony_ci	&tx2_pmu_event_attr_req_pktsent.attr.attr,
22762306a36Sopenharmony_ci	&tx2_pmu_event_attr_snoop_pktsent.attr.attr,
22862306a36Sopenharmony_ci	&tx2_pmu_event_attr_data_pktsent.attr.attr,
22962306a36Sopenharmony_ci	&tx2_pmu_event_attr_gic_pktsent.attr.attr,
23062306a36Sopenharmony_ci	NULL,
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic const struct attribute_group l3c_pmu_events_attr_group = {
23462306a36Sopenharmony_ci	.name = "events",
23562306a36Sopenharmony_ci	.attrs = l3c_pmu_events_attrs,
23662306a36Sopenharmony_ci};
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic const struct attribute_group dmc_pmu_events_attr_group = {
23962306a36Sopenharmony_ci	.name = "events",
24062306a36Sopenharmony_ci	.attrs = dmc_pmu_events_attrs,
24162306a36Sopenharmony_ci};
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic const struct attribute_group ccpi2_pmu_events_attr_group = {
24462306a36Sopenharmony_ci	.name = "events",
24562306a36Sopenharmony_ci	.attrs = ccpi2_pmu_events_attrs,
24662306a36Sopenharmony_ci};
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/*
24962306a36Sopenharmony_ci * sysfs cpumask attributes
25062306a36Sopenharmony_ci */
25162306a36Sopenharmony_cistatic ssize_t cpumask_show(struct device *dev, struct device_attribute *attr,
25262306a36Sopenharmony_ci		char *buf)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(dev_get_drvdata(dev));
25762306a36Sopenharmony_ci	return cpumap_print_to_pagebuf(true, buf, cpumask_of(tx2_pmu->cpu));
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(cpumask);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic struct attribute *tx2_pmu_cpumask_attrs[] = {
26262306a36Sopenharmony_ci	&dev_attr_cpumask.attr,
26362306a36Sopenharmony_ci	NULL,
26462306a36Sopenharmony_ci};
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic const struct attribute_group pmu_cpumask_attr_group = {
26762306a36Sopenharmony_ci	.attrs = tx2_pmu_cpumask_attrs,
26862306a36Sopenharmony_ci};
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/*
27162306a36Sopenharmony_ci * Per PMU device attribute groups
27262306a36Sopenharmony_ci */
27362306a36Sopenharmony_cistatic const struct attribute_group *l3c_pmu_attr_groups[] = {
27462306a36Sopenharmony_ci	&l3c_pmu_format_attr_group,
27562306a36Sopenharmony_ci	&pmu_cpumask_attr_group,
27662306a36Sopenharmony_ci	&l3c_pmu_events_attr_group,
27762306a36Sopenharmony_ci	NULL
27862306a36Sopenharmony_ci};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic const struct attribute_group *dmc_pmu_attr_groups[] = {
28162306a36Sopenharmony_ci	&dmc_pmu_format_attr_group,
28262306a36Sopenharmony_ci	&pmu_cpumask_attr_group,
28362306a36Sopenharmony_ci	&dmc_pmu_events_attr_group,
28462306a36Sopenharmony_ci	NULL
28562306a36Sopenharmony_ci};
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic const struct attribute_group *ccpi2_pmu_attr_groups[] = {
28862306a36Sopenharmony_ci	&ccpi2_pmu_format_attr_group,
28962306a36Sopenharmony_ci	&pmu_cpumask_attr_group,
29062306a36Sopenharmony_ci	&ccpi2_pmu_events_attr_group,
29162306a36Sopenharmony_ci	NULL
29262306a36Sopenharmony_ci};
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic inline u32 reg_readl(unsigned long addr)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	return readl((void __iomem *)addr);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic inline void reg_writel(u32 val, unsigned long addr)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	writel(val, (void __iomem *)addr);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int alloc_counter(struct tx2_uncore_pmu *tx2_pmu)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	int counter;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	counter = find_first_zero_bit(tx2_pmu->active_counters,
30962306a36Sopenharmony_ci				tx2_pmu->max_counters);
31062306a36Sopenharmony_ci	if (counter == tx2_pmu->max_counters)
31162306a36Sopenharmony_ci		return -ENOSPC;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	set_bit(counter, tx2_pmu->active_counters);
31462306a36Sopenharmony_ci	return counter;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic inline void free_counter(struct tx2_uncore_pmu *tx2_pmu, int counter)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	clear_bit(counter, tx2_pmu->active_counters);
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic void init_cntr_base_l3c(struct perf_event *event,
32362306a36Sopenharmony_ci		struct tx2_uncore_pmu *tx2_pmu)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
32662306a36Sopenharmony_ci	u32 cmask;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
32962306a36Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/* counter ctrl/data reg offset at 8 */
33262306a36Sopenharmony_ci	hwc->config_base = (unsigned long)tx2_pmu->base
33362306a36Sopenharmony_ci		+ L3C_COUNTER_CTL + (8 * GET_COUNTERID(event, cmask));
33462306a36Sopenharmony_ci	hwc->event_base =  (unsigned long)tx2_pmu->base
33562306a36Sopenharmony_ci		+ L3C_COUNTER_DATA + (8 * GET_COUNTERID(event, cmask));
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic void init_cntr_base_dmc(struct perf_event *event,
33962306a36Sopenharmony_ci		struct tx2_uncore_pmu *tx2_pmu)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
34262306a36Sopenharmony_ci	u32 cmask;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
34562306a36Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	hwc->config_base = (unsigned long)tx2_pmu->base
34862306a36Sopenharmony_ci		+ DMC_COUNTER_CTL;
34962306a36Sopenharmony_ci	/* counter data reg offset at 0xc */
35062306a36Sopenharmony_ci	hwc->event_base = (unsigned long)tx2_pmu->base
35162306a36Sopenharmony_ci		+ DMC_COUNTER_DATA + (0xc * GET_COUNTERID(event, cmask));
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic void init_cntr_base_ccpi2(struct perf_event *event,
35562306a36Sopenharmony_ci		struct tx2_uncore_pmu *tx2_pmu)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
35862306a36Sopenharmony_ci	u32 cmask;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	hwc->config_base = (unsigned long)tx2_pmu->base
36362306a36Sopenharmony_ci		+ CCPI2_COUNTER_CTL + (4 * GET_COUNTERID(event, cmask));
36462306a36Sopenharmony_ci	hwc->event_base =  (unsigned long)tx2_pmu->base;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic void uncore_start_event_l3c(struct perf_event *event, int flags)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	u32 val, emask;
37062306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
37162306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
37462306a36Sopenharmony_ci	emask = tx2_pmu->events_mask;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/* event id encoded in bits [07:03] */
37762306a36Sopenharmony_ci	val = GET_EVENTID(event, emask) << 3;
37862306a36Sopenharmony_ci	reg_writel(val, hwc->config_base);
37962306a36Sopenharmony_ci	local64_set(&hwc->prev_count, 0);
38062306a36Sopenharmony_ci	reg_writel(0, hwc->event_base);
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic inline void uncore_stop_event_l3c(struct perf_event *event)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	reg_writel(0, event->hw.config_base);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic void uncore_start_event_dmc(struct perf_event *event, int flags)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	u32 val, cmask, emask;
39162306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
39262306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
39362306a36Sopenharmony_ci	int idx, event_id;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
39662306a36Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
39762306a36Sopenharmony_ci	emask = tx2_pmu->events_mask;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	idx = GET_COUNTERID(event, cmask);
40062306a36Sopenharmony_ci	event_id = GET_EVENTID(event, emask);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* enable and start counters.
40362306a36Sopenharmony_ci	 * 8 bits for each counter, bits[05:01] of a counter to set event type.
40462306a36Sopenharmony_ci	 */
40562306a36Sopenharmony_ci	val = reg_readl(hwc->config_base);
40662306a36Sopenharmony_ci	val &= ~DMC_EVENT_CFG(idx, 0x1f);
40762306a36Sopenharmony_ci	val |= DMC_EVENT_CFG(idx, event_id);
40862306a36Sopenharmony_ci	reg_writel(val, hwc->config_base);
40962306a36Sopenharmony_ci	local64_set(&hwc->prev_count, 0);
41062306a36Sopenharmony_ci	reg_writel(0, hwc->event_base);
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic void uncore_stop_event_dmc(struct perf_event *event)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	u32 val, cmask;
41662306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
41762306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
41862306a36Sopenharmony_ci	int idx;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
42162306a36Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
42262306a36Sopenharmony_ci	idx = GET_COUNTERID(event, cmask);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	/* clear event type(bits[05:01]) to stop counter */
42562306a36Sopenharmony_ci	val = reg_readl(hwc->config_base);
42662306a36Sopenharmony_ci	val &= ~DMC_EVENT_CFG(idx, 0x1f);
42762306a36Sopenharmony_ci	reg_writel(val, hwc->config_base);
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic void uncore_start_event_ccpi2(struct perf_event *event, int flags)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	u32 emask;
43362306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
43462306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
43762306a36Sopenharmony_ci	emask = tx2_pmu->events_mask;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/* Bit [09:00] to set event id.
44062306a36Sopenharmony_ci	 * Bits [10], set level to rising edge.
44162306a36Sopenharmony_ci	 * Bits [11], set type to edge sensitive.
44262306a36Sopenharmony_ci	 */
44362306a36Sopenharmony_ci	reg_writel((CCPI2_EVENT_TYPE_EDGE_SENSITIVE |
44462306a36Sopenharmony_ci			CCPI2_EVENT_LEVEL_RISING_EDGE |
44562306a36Sopenharmony_ci			GET_EVENTID(event, emask)), hwc->config_base);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	/* reset[4], enable[0] and start[1] counters */
44862306a36Sopenharmony_ci	reg_writel(CCPI2_PERF_CTL_RESET |
44962306a36Sopenharmony_ci			CCPI2_PERF_CTL_START |
45062306a36Sopenharmony_ci			CCPI2_PERF_CTL_ENABLE,
45162306a36Sopenharmony_ci			hwc->event_base + CCPI2_PERF_CTL);
45262306a36Sopenharmony_ci	local64_set(&event->hw.prev_count, 0ULL);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic void uncore_stop_event_ccpi2(struct perf_event *event)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* disable and stop counter */
46062306a36Sopenharmony_ci	reg_writel(0, hwc->event_base + CCPI2_PERF_CTL);
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic void tx2_uncore_event_update(struct perf_event *event)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	u64 prev, delta, new = 0;
46662306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
46762306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
46862306a36Sopenharmony_ci	enum tx2_uncore_type type;
46962306a36Sopenharmony_ci	u32 prorate_factor;
47062306a36Sopenharmony_ci	u32 cmask, emask;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
47362306a36Sopenharmony_ci	type = tx2_pmu->type;
47462306a36Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
47562306a36Sopenharmony_ci	emask = tx2_pmu->events_mask;
47662306a36Sopenharmony_ci	prorate_factor = tx2_pmu->prorate_factor;
47762306a36Sopenharmony_ci	if (type == PMU_TYPE_CCPI2) {
47862306a36Sopenharmony_ci		reg_writel(CCPI2_COUNTER_OFFSET +
47962306a36Sopenharmony_ci				GET_COUNTERID(event, cmask),
48062306a36Sopenharmony_ci				hwc->event_base + CCPI2_COUNTER_SEL);
48162306a36Sopenharmony_ci		new = reg_readl(hwc->event_base + CCPI2_COUNTER_DATA_H);
48262306a36Sopenharmony_ci		new = (new << 32) +
48362306a36Sopenharmony_ci			reg_readl(hwc->event_base + CCPI2_COUNTER_DATA_L);
48462306a36Sopenharmony_ci		prev = local64_xchg(&hwc->prev_count, new);
48562306a36Sopenharmony_ci		delta = new - prev;
48662306a36Sopenharmony_ci	} else {
48762306a36Sopenharmony_ci		new = reg_readl(hwc->event_base);
48862306a36Sopenharmony_ci		prev = local64_xchg(&hwc->prev_count, new);
48962306a36Sopenharmony_ci		/* handles rollover of 32 bit counter */
49062306a36Sopenharmony_ci		delta = (u32)(((1ULL << 32) - prev) + new);
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* DMC event data_transfers granularity is 16 Bytes, convert it to 64 */
49462306a36Sopenharmony_ci	if (type == PMU_TYPE_DMC &&
49562306a36Sopenharmony_ci			GET_EVENTID(event, emask) == DMC_EVENT_DATA_TRANSFERS)
49662306a36Sopenharmony_ci		delta = delta/4;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	/* L3C and DMC has 16 and 8 interleave channels respectively.
49962306a36Sopenharmony_ci	 * The sampled value is for channel 0 and multiplied with
50062306a36Sopenharmony_ci	 * prorate_factor to get the count for a device.
50162306a36Sopenharmony_ci	 */
50262306a36Sopenharmony_ci	local64_add(delta * prorate_factor, &event->count);
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic enum tx2_uncore_type get_tx2_pmu_type(struct acpi_device *adev)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	int i = 0;
50862306a36Sopenharmony_ci	struct acpi_tx2_pmu_device {
50962306a36Sopenharmony_ci		__u8 id[ACPI_ID_LEN];
51062306a36Sopenharmony_ci		enum tx2_uncore_type type;
51162306a36Sopenharmony_ci	} devices[] = {
51262306a36Sopenharmony_ci		{"CAV901D", PMU_TYPE_L3C},
51362306a36Sopenharmony_ci		{"CAV901F", PMU_TYPE_DMC},
51462306a36Sopenharmony_ci		{"CAV901E", PMU_TYPE_CCPI2},
51562306a36Sopenharmony_ci		{"", PMU_TYPE_INVALID}
51662306a36Sopenharmony_ci	};
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	while (devices[i].type != PMU_TYPE_INVALID) {
51962306a36Sopenharmony_ci		if (!strcmp(acpi_device_hid(adev), devices[i].id))
52062306a36Sopenharmony_ci			break;
52162306a36Sopenharmony_ci		i++;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return devices[i].type;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic bool tx2_uncore_validate_event(struct pmu *pmu,
52862306a36Sopenharmony_ci				  struct perf_event *event, int *counters)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	if (is_software_event(event))
53162306a36Sopenharmony_ci		return true;
53262306a36Sopenharmony_ci	/* Reject groups spanning multiple HW PMUs. */
53362306a36Sopenharmony_ci	if (event->pmu != pmu)
53462306a36Sopenharmony_ci		return false;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	*counters = *counters + 1;
53762306a36Sopenharmony_ci	return true;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci/*
54162306a36Sopenharmony_ci * Make sure the group of events can be scheduled at once
54262306a36Sopenharmony_ci * on the PMU.
54362306a36Sopenharmony_ci */
54462306a36Sopenharmony_cistatic bool tx2_uncore_validate_event_group(struct perf_event *event,
54562306a36Sopenharmony_ci		int max_counters)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct perf_event *sibling, *leader = event->group_leader;
54862306a36Sopenharmony_ci	int counters = 0;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (event->group_leader == event)
55162306a36Sopenharmony_ci		return true;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (!tx2_uncore_validate_event(event->pmu, leader, &counters))
55462306a36Sopenharmony_ci		return false;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	for_each_sibling_event(sibling, leader) {
55762306a36Sopenharmony_ci		if (!tx2_uncore_validate_event(event->pmu, sibling, &counters))
55862306a36Sopenharmony_ci			return false;
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (!tx2_uncore_validate_event(event->pmu, event, &counters))
56262306a36Sopenharmony_ci		return false;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/*
56562306a36Sopenharmony_ci	 * If the group requires more counters than the HW has,
56662306a36Sopenharmony_ci	 * it cannot ever be scheduled.
56762306a36Sopenharmony_ci	 */
56862306a36Sopenharmony_ci	return counters <= max_counters;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic int tx2_uncore_event_init(struct perf_event *event)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
57562306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	/* Test the event attr type check for PMU enumeration */
57862306a36Sopenharmony_ci	if (event->attr.type != event->pmu->type)
57962306a36Sopenharmony_ci		return -ENOENT;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	/*
58262306a36Sopenharmony_ci	 * SOC PMU counters are shared across all cores.
58362306a36Sopenharmony_ci	 * Therefore, it does not support per-process mode.
58462306a36Sopenharmony_ci	 * Also, it does not support event sampling mode.
58562306a36Sopenharmony_ci	 */
58662306a36Sopenharmony_ci	if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
58762306a36Sopenharmony_ci		return -EINVAL;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (event->cpu < 0)
59062306a36Sopenharmony_ci		return -EINVAL;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
59362306a36Sopenharmony_ci	if (tx2_pmu->cpu >= nr_cpu_ids)
59462306a36Sopenharmony_ci		return -EINVAL;
59562306a36Sopenharmony_ci	event->cpu = tx2_pmu->cpu;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (event->attr.config >= tx2_pmu->max_events)
59862306a36Sopenharmony_ci		return -EINVAL;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* store event id */
60162306a36Sopenharmony_ci	hwc->config = event->attr.config;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* Validate the group */
60462306a36Sopenharmony_ci	if (!tx2_uncore_validate_event_group(event, tx2_pmu->max_counters))
60562306a36Sopenharmony_ci		return -EINVAL;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	return 0;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic void tx2_uncore_event_start(struct perf_event *event, int flags)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
61362306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	hwc->state = 0;
61662306a36Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	tx2_pmu->start_event(event, flags);
61962306a36Sopenharmony_ci	perf_event_update_userpage(event);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	/* No hrtimer needed for CCPI2, 64-bit counters */
62262306a36Sopenharmony_ci	if (!tx2_pmu->hrtimer_callback)
62362306a36Sopenharmony_ci		return;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	/* Start timer for first event */
62662306a36Sopenharmony_ci	if (bitmap_weight(tx2_pmu->active_counters,
62762306a36Sopenharmony_ci				tx2_pmu->max_counters) == 1) {
62862306a36Sopenharmony_ci		hrtimer_start(&tx2_pmu->hrtimer,
62962306a36Sopenharmony_ci			ns_to_ktime(tx2_pmu->hrtimer_interval),
63062306a36Sopenharmony_ci			HRTIMER_MODE_REL_PINNED);
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic void tx2_uncore_event_stop(struct perf_event *event, int flags)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
63762306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (hwc->state & PERF_HES_UPTODATE)
64062306a36Sopenharmony_ci		return;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
64362306a36Sopenharmony_ci	tx2_pmu->stop_event(event);
64462306a36Sopenharmony_ci	WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
64562306a36Sopenharmony_ci	hwc->state |= PERF_HES_STOPPED;
64662306a36Sopenharmony_ci	if (flags & PERF_EF_UPDATE) {
64762306a36Sopenharmony_ci		tx2_uncore_event_update(event);
64862306a36Sopenharmony_ci		hwc->state |= PERF_HES_UPTODATE;
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic int tx2_uncore_event_add(struct perf_event *event, int flags)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
65562306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* Allocate a free counter */
66062306a36Sopenharmony_ci	hwc->idx  = alloc_counter(tx2_pmu);
66162306a36Sopenharmony_ci	if (hwc->idx < 0)
66262306a36Sopenharmony_ci		return -EAGAIN;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	tx2_pmu->events[hwc->idx] = event;
66562306a36Sopenharmony_ci	/* set counter control and data registers base address */
66662306a36Sopenharmony_ci	tx2_pmu->init_cntr_base(event, tx2_pmu);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
66962306a36Sopenharmony_ci	if (flags & PERF_EF_START)
67062306a36Sopenharmony_ci		tx2_uncore_event_start(event, flags);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	return 0;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic void tx2_uncore_event_del(struct perf_event *event, int flags)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu = pmu_to_tx2_pmu(event->pmu);
67862306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
67962306a36Sopenharmony_ci	u32 cmask;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
68262306a36Sopenharmony_ci	tx2_uncore_event_stop(event, PERF_EF_UPDATE);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	/* clear the assigned counter */
68562306a36Sopenharmony_ci	free_counter(tx2_pmu, GET_COUNTERID(event, cmask));
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	perf_event_update_userpage(event);
68862306a36Sopenharmony_ci	tx2_pmu->events[hwc->idx] = NULL;
68962306a36Sopenharmony_ci	hwc->idx = -1;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (!tx2_pmu->hrtimer_callback)
69262306a36Sopenharmony_ci		return;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (bitmap_empty(tx2_pmu->active_counters, tx2_pmu->max_counters))
69562306a36Sopenharmony_ci		hrtimer_cancel(&tx2_pmu->hrtimer);
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic void tx2_uncore_event_read(struct perf_event *event)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	tx2_uncore_event_update(event);
70162306a36Sopenharmony_ci}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_cistatic enum hrtimer_restart tx2_hrtimer_callback(struct hrtimer *timer)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
70662306a36Sopenharmony_ci	int max_counters, idx;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	tx2_pmu = container_of(timer, struct tx2_uncore_pmu, hrtimer);
70962306a36Sopenharmony_ci	max_counters = tx2_pmu->max_counters;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	if (bitmap_empty(tx2_pmu->active_counters, max_counters))
71262306a36Sopenharmony_ci		return HRTIMER_NORESTART;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	for_each_set_bit(idx, tx2_pmu->active_counters, max_counters) {
71562306a36Sopenharmony_ci		struct perf_event *event = tx2_pmu->events[idx];
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci		tx2_uncore_event_update(event);
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci	hrtimer_forward_now(timer, ns_to_ktime(tx2_pmu->hrtimer_interval));
72062306a36Sopenharmony_ci	return HRTIMER_RESTART;
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic int tx2_uncore_pmu_register(
72462306a36Sopenharmony_ci		struct tx2_uncore_pmu *tx2_pmu)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	struct device *dev = tx2_pmu->dev;
72762306a36Sopenharmony_ci	char *name = tx2_pmu->name;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	/* Perf event registration */
73062306a36Sopenharmony_ci	tx2_pmu->pmu = (struct pmu) {
73162306a36Sopenharmony_ci		.module         = THIS_MODULE,
73262306a36Sopenharmony_ci		.attr_groups	= tx2_pmu->attr_groups,
73362306a36Sopenharmony_ci		.task_ctx_nr	= perf_invalid_context,
73462306a36Sopenharmony_ci		.event_init	= tx2_uncore_event_init,
73562306a36Sopenharmony_ci		.add		= tx2_uncore_event_add,
73662306a36Sopenharmony_ci		.del		= tx2_uncore_event_del,
73762306a36Sopenharmony_ci		.start		= tx2_uncore_event_start,
73862306a36Sopenharmony_ci		.stop		= tx2_uncore_event_stop,
73962306a36Sopenharmony_ci		.read		= tx2_uncore_event_read,
74062306a36Sopenharmony_ci		.capabilities	= PERF_PMU_CAP_NO_EXCLUDE,
74162306a36Sopenharmony_ci	};
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	tx2_pmu->pmu.name = devm_kasprintf(dev, GFP_KERNEL,
74462306a36Sopenharmony_ci			"%s", name);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	return perf_pmu_register(&tx2_pmu->pmu, tx2_pmu->pmu.name, -1);
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic int tx2_uncore_pmu_add_dev(struct tx2_uncore_pmu *tx2_pmu)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	int ret, cpu;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	cpu = cpumask_any_and(cpumask_of_node(tx2_pmu->node),
75462306a36Sopenharmony_ci			cpu_online_mask);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	tx2_pmu->cpu = cpu;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (tx2_pmu->hrtimer_callback) {
75962306a36Sopenharmony_ci		hrtimer_init(&tx2_pmu->hrtimer,
76062306a36Sopenharmony_ci				CLOCK_MONOTONIC, HRTIMER_MODE_REL);
76162306a36Sopenharmony_ci		tx2_pmu->hrtimer.function = tx2_pmu->hrtimer_callback;
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	ret = tx2_uncore_pmu_register(tx2_pmu);
76562306a36Sopenharmony_ci	if (ret) {
76662306a36Sopenharmony_ci		dev_err(tx2_pmu->dev, "%s PMU: Failed to init driver\n",
76762306a36Sopenharmony_ci				tx2_pmu->name);
76862306a36Sopenharmony_ci		return -ENODEV;
76962306a36Sopenharmony_ci	}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	/* register hotplug callback for the pmu */
77262306a36Sopenharmony_ci	ret = cpuhp_state_add_instance(
77362306a36Sopenharmony_ci			CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE,
77462306a36Sopenharmony_ci			&tx2_pmu->hpnode);
77562306a36Sopenharmony_ci	if (ret) {
77662306a36Sopenharmony_ci		dev_err(tx2_pmu->dev, "Error %d registering hotplug", ret);
77762306a36Sopenharmony_ci		return ret;
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	/* Add to list */
78162306a36Sopenharmony_ci	list_add(&tx2_pmu->entry, &tx2_pmus);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	dev_dbg(tx2_pmu->dev, "%s PMU UNCORE registered\n",
78462306a36Sopenharmony_ci			tx2_pmu->pmu.name);
78562306a36Sopenharmony_ci	return ret;
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic struct tx2_uncore_pmu *tx2_uncore_pmu_init_dev(struct device *dev,
78962306a36Sopenharmony_ci		acpi_handle handle, struct acpi_device *adev, u32 type)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
79262306a36Sopenharmony_ci	void __iomem *base;
79362306a36Sopenharmony_ci	struct resource res;
79462306a36Sopenharmony_ci	struct resource_entry *rentry;
79562306a36Sopenharmony_ci	struct list_head list;
79662306a36Sopenharmony_ci	int ret;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	INIT_LIST_HEAD(&list);
79962306a36Sopenharmony_ci	ret = acpi_dev_get_resources(adev, &list, NULL, NULL);
80062306a36Sopenharmony_ci	if (ret <= 0) {
80162306a36Sopenharmony_ci		dev_err(dev, "failed to parse _CRS method, error %d\n", ret);
80262306a36Sopenharmony_ci		return NULL;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	list_for_each_entry(rentry, &list, node) {
80662306a36Sopenharmony_ci		if (resource_type(rentry->res) == IORESOURCE_MEM) {
80762306a36Sopenharmony_ci			res = *rentry->res;
80862306a36Sopenharmony_ci			rentry = NULL;
80962306a36Sopenharmony_ci			break;
81062306a36Sopenharmony_ci		}
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci	acpi_dev_free_resource_list(&list);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	if (rentry) {
81562306a36Sopenharmony_ci		dev_err(dev, "PMU type %d: Fail to find resource\n", type);
81662306a36Sopenharmony_ci		return NULL;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	base = devm_ioremap_resource(dev, &res);
82062306a36Sopenharmony_ci	if (IS_ERR(base))
82162306a36Sopenharmony_ci		return NULL;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	tx2_pmu = devm_kzalloc(dev, sizeof(*tx2_pmu), GFP_KERNEL);
82462306a36Sopenharmony_ci	if (!tx2_pmu)
82562306a36Sopenharmony_ci		return NULL;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	tx2_pmu->dev = dev;
82862306a36Sopenharmony_ci	tx2_pmu->type = type;
82962306a36Sopenharmony_ci	tx2_pmu->base = base;
83062306a36Sopenharmony_ci	tx2_pmu->node = dev_to_node(dev);
83162306a36Sopenharmony_ci	INIT_LIST_HEAD(&tx2_pmu->entry);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	switch (tx2_pmu->type) {
83462306a36Sopenharmony_ci	case PMU_TYPE_L3C:
83562306a36Sopenharmony_ci		tx2_pmu->max_counters = TX2_PMU_DMC_L3C_MAX_COUNTERS;
83662306a36Sopenharmony_ci		tx2_pmu->counters_mask = 0x3;
83762306a36Sopenharmony_ci		tx2_pmu->prorate_factor = TX2_PMU_L3_TILES;
83862306a36Sopenharmony_ci		tx2_pmu->max_events = L3_EVENT_MAX;
83962306a36Sopenharmony_ci		tx2_pmu->events_mask = 0x1f;
84062306a36Sopenharmony_ci		tx2_pmu->hrtimer_interval = TX2_PMU_HRTIMER_INTERVAL;
84162306a36Sopenharmony_ci		tx2_pmu->hrtimer_callback = tx2_hrtimer_callback;
84262306a36Sopenharmony_ci		tx2_pmu->attr_groups = l3c_pmu_attr_groups;
84362306a36Sopenharmony_ci		tx2_pmu->name = devm_kasprintf(dev, GFP_KERNEL,
84462306a36Sopenharmony_ci				"uncore_l3c_%d", tx2_pmu->node);
84562306a36Sopenharmony_ci		tx2_pmu->init_cntr_base = init_cntr_base_l3c;
84662306a36Sopenharmony_ci		tx2_pmu->start_event = uncore_start_event_l3c;
84762306a36Sopenharmony_ci		tx2_pmu->stop_event = uncore_stop_event_l3c;
84862306a36Sopenharmony_ci		break;
84962306a36Sopenharmony_ci	case PMU_TYPE_DMC:
85062306a36Sopenharmony_ci		tx2_pmu->max_counters = TX2_PMU_DMC_L3C_MAX_COUNTERS;
85162306a36Sopenharmony_ci		tx2_pmu->counters_mask = 0x3;
85262306a36Sopenharmony_ci		tx2_pmu->prorate_factor = TX2_PMU_DMC_CHANNELS;
85362306a36Sopenharmony_ci		tx2_pmu->max_events = DMC_EVENT_MAX;
85462306a36Sopenharmony_ci		tx2_pmu->events_mask = 0x1f;
85562306a36Sopenharmony_ci		tx2_pmu->hrtimer_interval = TX2_PMU_HRTIMER_INTERVAL;
85662306a36Sopenharmony_ci		tx2_pmu->hrtimer_callback = tx2_hrtimer_callback;
85762306a36Sopenharmony_ci		tx2_pmu->attr_groups = dmc_pmu_attr_groups;
85862306a36Sopenharmony_ci		tx2_pmu->name = devm_kasprintf(dev, GFP_KERNEL,
85962306a36Sopenharmony_ci				"uncore_dmc_%d", tx2_pmu->node);
86062306a36Sopenharmony_ci		tx2_pmu->init_cntr_base = init_cntr_base_dmc;
86162306a36Sopenharmony_ci		tx2_pmu->start_event = uncore_start_event_dmc;
86262306a36Sopenharmony_ci		tx2_pmu->stop_event = uncore_stop_event_dmc;
86362306a36Sopenharmony_ci		break;
86462306a36Sopenharmony_ci	case PMU_TYPE_CCPI2:
86562306a36Sopenharmony_ci		/* CCPI2 has 8 counters */
86662306a36Sopenharmony_ci		tx2_pmu->max_counters = TX2_PMU_CCPI2_MAX_COUNTERS;
86762306a36Sopenharmony_ci		tx2_pmu->counters_mask = 0x7;
86862306a36Sopenharmony_ci		tx2_pmu->prorate_factor = 1;
86962306a36Sopenharmony_ci		tx2_pmu->max_events = CCPI2_EVENT_MAX;
87062306a36Sopenharmony_ci		tx2_pmu->events_mask = 0x1ff;
87162306a36Sopenharmony_ci		tx2_pmu->attr_groups = ccpi2_pmu_attr_groups;
87262306a36Sopenharmony_ci		tx2_pmu->name = devm_kasprintf(dev, GFP_KERNEL,
87362306a36Sopenharmony_ci				"uncore_ccpi2_%d", tx2_pmu->node);
87462306a36Sopenharmony_ci		tx2_pmu->init_cntr_base = init_cntr_base_ccpi2;
87562306a36Sopenharmony_ci		tx2_pmu->start_event = uncore_start_event_ccpi2;
87662306a36Sopenharmony_ci		tx2_pmu->stop_event = uncore_stop_event_ccpi2;
87762306a36Sopenharmony_ci		tx2_pmu->hrtimer_callback = NULL;
87862306a36Sopenharmony_ci		break;
87962306a36Sopenharmony_ci	case PMU_TYPE_INVALID:
88062306a36Sopenharmony_ci		devm_kfree(dev, tx2_pmu);
88162306a36Sopenharmony_ci		return NULL;
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	return tx2_pmu;
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic acpi_status tx2_uncore_pmu_add(acpi_handle handle, u32 level,
88862306a36Sopenharmony_ci				    void *data, void **return_value)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
89162306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
89262306a36Sopenharmony_ci	enum tx2_uncore_type type;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	if (!adev || acpi_bus_get_status(adev) || !adev->status.present)
89562306a36Sopenharmony_ci		return AE_OK;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	type = get_tx2_pmu_type(adev);
89862306a36Sopenharmony_ci	if (type == PMU_TYPE_INVALID)
89962306a36Sopenharmony_ci		return AE_OK;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	tx2_pmu = tx2_uncore_pmu_init_dev((struct device *)data,
90262306a36Sopenharmony_ci			handle, adev, type);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	if (!tx2_pmu)
90562306a36Sopenharmony_ci		return AE_ERROR;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (tx2_uncore_pmu_add_dev(tx2_pmu)) {
90862306a36Sopenharmony_ci		/* Can't add the PMU device, abort */
90962306a36Sopenharmony_ci		return AE_ERROR;
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci	return AE_OK;
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic int tx2_uncore_pmu_online_cpu(unsigned int cpu,
91562306a36Sopenharmony_ci		struct hlist_node *hpnode)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	tx2_pmu = hlist_entry_safe(hpnode,
92062306a36Sopenharmony_ci			struct tx2_uncore_pmu, hpnode);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	/* Pick this CPU, If there is no CPU/PMU association and both are
92362306a36Sopenharmony_ci	 * from same node.
92462306a36Sopenharmony_ci	 */
92562306a36Sopenharmony_ci	if ((tx2_pmu->cpu >= nr_cpu_ids) &&
92662306a36Sopenharmony_ci		(tx2_pmu->node == cpu_to_node(cpu)))
92762306a36Sopenharmony_ci		tx2_pmu->cpu = cpu;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	return 0;
93062306a36Sopenharmony_ci}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_cistatic int tx2_uncore_pmu_offline_cpu(unsigned int cpu,
93362306a36Sopenharmony_ci		struct hlist_node *hpnode)
93462306a36Sopenharmony_ci{
93562306a36Sopenharmony_ci	int new_cpu;
93662306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
93762306a36Sopenharmony_ci	struct cpumask cpu_online_mask_temp;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	tx2_pmu = hlist_entry_safe(hpnode,
94062306a36Sopenharmony_ci			struct tx2_uncore_pmu, hpnode);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	if (cpu != tx2_pmu->cpu)
94362306a36Sopenharmony_ci		return 0;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (tx2_pmu->hrtimer_callback)
94662306a36Sopenharmony_ci		hrtimer_cancel(&tx2_pmu->hrtimer);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	cpumask_copy(&cpu_online_mask_temp, cpu_online_mask);
94962306a36Sopenharmony_ci	cpumask_clear_cpu(cpu, &cpu_online_mask_temp);
95062306a36Sopenharmony_ci	new_cpu = cpumask_any_and(
95162306a36Sopenharmony_ci			cpumask_of_node(tx2_pmu->node),
95262306a36Sopenharmony_ci			&cpu_online_mask_temp);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	tx2_pmu->cpu = new_cpu;
95562306a36Sopenharmony_ci	if (new_cpu >= nr_cpu_ids)
95662306a36Sopenharmony_ci		return 0;
95762306a36Sopenharmony_ci	perf_pmu_migrate_context(&tx2_pmu->pmu, cpu, new_cpu);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	return 0;
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic const struct acpi_device_id tx2_uncore_acpi_match[] = {
96362306a36Sopenharmony_ci	{"CAV901C", 0},
96462306a36Sopenharmony_ci	{},
96562306a36Sopenharmony_ci};
96662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, tx2_uncore_acpi_match);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic int tx2_uncore_probe(struct platform_device *pdev)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
97162306a36Sopenharmony_ci	acpi_handle handle;
97262306a36Sopenharmony_ci	acpi_status status;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	set_dev_node(dev, acpi_get_node(ACPI_HANDLE(dev)));
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	if (!has_acpi_companion(dev))
97762306a36Sopenharmony_ci		return -ENODEV;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	handle = ACPI_HANDLE(dev);
98062306a36Sopenharmony_ci	if (!handle)
98162306a36Sopenharmony_ci		return -EINVAL;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	/* Walk through the tree for all PMU UNCORE devices */
98462306a36Sopenharmony_ci	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
98562306a36Sopenharmony_ci				     tx2_uncore_pmu_add,
98662306a36Sopenharmony_ci				     NULL, dev, NULL);
98762306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
98862306a36Sopenharmony_ci		dev_err(dev, "failed to probe PMU devices\n");
98962306a36Sopenharmony_ci		return_ACPI_STATUS(status);
99062306a36Sopenharmony_ci	}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	dev_info(dev, "node%d: pmu uncore registered\n", dev_to_node(dev));
99362306a36Sopenharmony_ci	return 0;
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic int tx2_uncore_remove(struct platform_device *pdev)
99762306a36Sopenharmony_ci{
99862306a36Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu, *temp;
99962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	if (!list_empty(&tx2_pmus)) {
100262306a36Sopenharmony_ci		list_for_each_entry_safe(tx2_pmu, temp, &tx2_pmus, entry) {
100362306a36Sopenharmony_ci			if (tx2_pmu->node == dev_to_node(dev)) {
100462306a36Sopenharmony_ci				cpuhp_state_remove_instance_nocalls(
100562306a36Sopenharmony_ci					CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE,
100662306a36Sopenharmony_ci					&tx2_pmu->hpnode);
100762306a36Sopenharmony_ci				perf_pmu_unregister(&tx2_pmu->pmu);
100862306a36Sopenharmony_ci				list_del(&tx2_pmu->entry);
100962306a36Sopenharmony_ci			}
101062306a36Sopenharmony_ci		}
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci	return 0;
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_cistatic struct platform_driver tx2_uncore_driver = {
101662306a36Sopenharmony_ci	.driver = {
101762306a36Sopenharmony_ci		.name		= "tx2-uncore-pmu",
101862306a36Sopenharmony_ci		.acpi_match_table = ACPI_PTR(tx2_uncore_acpi_match),
101962306a36Sopenharmony_ci		.suppress_bind_attrs = true,
102062306a36Sopenharmony_ci	},
102162306a36Sopenharmony_ci	.probe = tx2_uncore_probe,
102262306a36Sopenharmony_ci	.remove = tx2_uncore_remove,
102362306a36Sopenharmony_ci};
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_cistatic int __init tx2_uncore_driver_init(void)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	int ret;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE,
103062306a36Sopenharmony_ci				      "perf/tx2/uncore:online",
103162306a36Sopenharmony_ci				      tx2_uncore_pmu_online_cpu,
103262306a36Sopenharmony_ci				      tx2_uncore_pmu_offline_cpu);
103362306a36Sopenharmony_ci	if (ret) {
103462306a36Sopenharmony_ci		pr_err("TX2 PMU: setup hotplug failed(%d)\n", ret);
103562306a36Sopenharmony_ci		return ret;
103662306a36Sopenharmony_ci	}
103762306a36Sopenharmony_ci	ret = platform_driver_register(&tx2_uncore_driver);
103862306a36Sopenharmony_ci	if (ret)
103962306a36Sopenharmony_ci		cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	return ret;
104262306a36Sopenharmony_ci}
104362306a36Sopenharmony_cimodule_init(tx2_uncore_driver_init);
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic void __exit tx2_uncore_driver_exit(void)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	platform_driver_unregister(&tx2_uncore_driver);
104862306a36Sopenharmony_ci	cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE);
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_cimodule_exit(tx2_uncore_driver_exit);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ciMODULE_DESCRIPTION("ThunderX2 UNCORE PMU driver");
105362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
105462306a36Sopenharmony_ciMODULE_AUTHOR("Ganapatrao Kulkarni <gkulkarni@cavium.com>");
1055