18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * CAVIUM THUNDERX2 SoC PMU UNCORE
48c2ecf20Sopenharmony_ci * Copyright (C) 2018 Cavium Inc.
58c2ecf20Sopenharmony_ci * Author: Ganapatrao Kulkarni <gkulkarni@cavium.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/acpi.h>
98c2ecf20Sopenharmony_ci#include <linux/cpuhotplug.h>
108c2ecf20Sopenharmony_ci#include <linux/perf_event.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/* Each ThunderX2(TX2) Socket has a L3C and DMC UNCORE PMU device.
148c2ecf20Sopenharmony_ci * Each UNCORE PMU device consists of 4 independent programmable counters.
158c2ecf20Sopenharmony_ci * Counters are 32 bit and do not support overflow interrupt,
168c2ecf20Sopenharmony_ci * they need to be sampled before overflow(i.e, at every 2 seconds).
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define TX2_PMU_DMC_L3C_MAX_COUNTERS	4
208c2ecf20Sopenharmony_ci#define TX2_PMU_CCPI2_MAX_COUNTERS	8
218c2ecf20Sopenharmony_ci#define TX2_PMU_MAX_COUNTERS		TX2_PMU_CCPI2_MAX_COUNTERS
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define TX2_PMU_DMC_CHANNELS		8
258c2ecf20Sopenharmony_ci#define TX2_PMU_L3_TILES		16
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define TX2_PMU_HRTIMER_INTERVAL	(2 * NSEC_PER_SEC)
288c2ecf20Sopenharmony_ci#define GET_EVENTID(ev, mask)		((ev->hw.config) & mask)
298c2ecf20Sopenharmony_ci#define GET_COUNTERID(ev, mask)		((ev->hw.idx) & mask)
308c2ecf20Sopenharmony_ci /* 1 byte per counter(4 counters).
318c2ecf20Sopenharmony_ci  * Event id is encoded in bits [5:1] of a byte,
328c2ecf20Sopenharmony_ci  */
338c2ecf20Sopenharmony_ci#define DMC_EVENT_CFG(idx, val)		((val) << (((idx) * 8) + 1))
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* bits[3:0] to select counters, are indexed from 8 to 15. */
368c2ecf20Sopenharmony_ci#define CCPI2_COUNTER_OFFSET		8
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define L3C_COUNTER_CTL			0xA8
398c2ecf20Sopenharmony_ci#define L3C_COUNTER_DATA		0xAC
408c2ecf20Sopenharmony_ci#define DMC_COUNTER_CTL			0x234
418c2ecf20Sopenharmony_ci#define DMC_COUNTER_DATA		0x240
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define CCPI2_PERF_CTL			0x108
448c2ecf20Sopenharmony_ci#define CCPI2_COUNTER_CTL		0x10C
458c2ecf20Sopenharmony_ci#define CCPI2_COUNTER_SEL		0x12c
468c2ecf20Sopenharmony_ci#define CCPI2_COUNTER_DATA_L		0x130
478c2ecf20Sopenharmony_ci#define CCPI2_COUNTER_DATA_H		0x134
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* L3C event IDs */
508c2ecf20Sopenharmony_ci#define L3_EVENT_READ_REQ		0xD
518c2ecf20Sopenharmony_ci#define L3_EVENT_WRITEBACK_REQ		0xE
528c2ecf20Sopenharmony_ci#define L3_EVENT_INV_N_WRITE_REQ	0xF
538c2ecf20Sopenharmony_ci#define L3_EVENT_INV_REQ		0x10
548c2ecf20Sopenharmony_ci#define L3_EVENT_EVICT_REQ		0x13
558c2ecf20Sopenharmony_ci#define L3_EVENT_INV_N_WRITE_HIT	0x14
568c2ecf20Sopenharmony_ci#define L3_EVENT_INV_HIT		0x15
578c2ecf20Sopenharmony_ci#define L3_EVENT_READ_HIT		0x17
588c2ecf20Sopenharmony_ci#define L3_EVENT_MAX			0x18
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/* DMC event IDs */
618c2ecf20Sopenharmony_ci#define DMC_EVENT_COUNT_CYCLES		0x1
628c2ecf20Sopenharmony_ci#define DMC_EVENT_WRITE_TXNS		0xB
638c2ecf20Sopenharmony_ci#define DMC_EVENT_DATA_TRANSFERS	0xD
648c2ecf20Sopenharmony_ci#define DMC_EVENT_READ_TXNS		0xF
658c2ecf20Sopenharmony_ci#define DMC_EVENT_MAX			0x10
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define CCPI2_EVENT_REQ_PKT_SENT	0x3D
688c2ecf20Sopenharmony_ci#define CCPI2_EVENT_SNOOP_PKT_SENT	0x65
698c2ecf20Sopenharmony_ci#define CCPI2_EVENT_DATA_PKT_SENT	0x105
708c2ecf20Sopenharmony_ci#define CCPI2_EVENT_GIC_PKT_SENT	0x12D
718c2ecf20Sopenharmony_ci#define CCPI2_EVENT_MAX			0x200
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define CCPI2_PERF_CTL_ENABLE		BIT(0)
748c2ecf20Sopenharmony_ci#define CCPI2_PERF_CTL_START		BIT(1)
758c2ecf20Sopenharmony_ci#define CCPI2_PERF_CTL_RESET		BIT(4)
768c2ecf20Sopenharmony_ci#define CCPI2_EVENT_LEVEL_RISING_EDGE	BIT(10)
778c2ecf20Sopenharmony_ci#define CCPI2_EVENT_TYPE_EDGE_SENSITIVE	BIT(11)
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cienum tx2_uncore_type {
808c2ecf20Sopenharmony_ci	PMU_TYPE_L3C,
818c2ecf20Sopenharmony_ci	PMU_TYPE_DMC,
828c2ecf20Sopenharmony_ci	PMU_TYPE_CCPI2,
838c2ecf20Sopenharmony_ci	PMU_TYPE_INVALID,
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/*
878c2ecf20Sopenharmony_ci * Each socket has 3 uncore devices associated with a PMU. The DMC and
888c2ecf20Sopenharmony_ci * L3C have 4 32-bit counters and the CCPI2 has 8 64-bit counters.
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_cistruct tx2_uncore_pmu {
918c2ecf20Sopenharmony_ci	struct hlist_node hpnode;
928c2ecf20Sopenharmony_ci	struct list_head  entry;
938c2ecf20Sopenharmony_ci	struct pmu pmu;
948c2ecf20Sopenharmony_ci	char *name;
958c2ecf20Sopenharmony_ci	int node;
968c2ecf20Sopenharmony_ci	int cpu;
978c2ecf20Sopenharmony_ci	u32 max_counters;
988c2ecf20Sopenharmony_ci	u32 counters_mask;
998c2ecf20Sopenharmony_ci	u32 prorate_factor;
1008c2ecf20Sopenharmony_ci	u32 max_events;
1018c2ecf20Sopenharmony_ci	u32 events_mask;
1028c2ecf20Sopenharmony_ci	u64 hrtimer_interval;
1038c2ecf20Sopenharmony_ci	void __iomem *base;
1048c2ecf20Sopenharmony_ci	DECLARE_BITMAP(active_counters, TX2_PMU_MAX_COUNTERS);
1058c2ecf20Sopenharmony_ci	struct perf_event *events[TX2_PMU_MAX_COUNTERS];
1068c2ecf20Sopenharmony_ci	struct device *dev;
1078c2ecf20Sopenharmony_ci	struct hrtimer hrtimer;
1088c2ecf20Sopenharmony_ci	const struct attribute_group **attr_groups;
1098c2ecf20Sopenharmony_ci	enum tx2_uncore_type type;
1108c2ecf20Sopenharmony_ci	enum hrtimer_restart (*hrtimer_callback)(struct hrtimer *cb);
1118c2ecf20Sopenharmony_ci	void (*init_cntr_base)(struct perf_event *event,
1128c2ecf20Sopenharmony_ci			struct tx2_uncore_pmu *tx2_pmu);
1138c2ecf20Sopenharmony_ci	void (*stop_event)(struct perf_event *event);
1148c2ecf20Sopenharmony_ci	void (*start_event)(struct perf_event *event, int flags);
1158c2ecf20Sopenharmony_ci};
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic LIST_HEAD(tx2_pmus);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic inline struct tx2_uncore_pmu *pmu_to_tx2_pmu(struct pmu *pmu)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	return container_of(pmu, struct tx2_uncore_pmu, pmu);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#define TX2_PMU_FORMAT_ATTR(_var, _name, _format)			\
1258c2ecf20Sopenharmony_cistatic ssize_t								\
1268c2ecf20Sopenharmony_ci__tx2_pmu_##_var##_show(struct device *dev,				\
1278c2ecf20Sopenharmony_ci			       struct device_attribute *attr,		\
1288c2ecf20Sopenharmony_ci			       char *page)				\
1298c2ecf20Sopenharmony_ci{									\
1308c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE);			\
1318c2ecf20Sopenharmony_ci	return sprintf(page, _format "\n");				\
1328c2ecf20Sopenharmony_ci}									\
1338c2ecf20Sopenharmony_ci									\
1348c2ecf20Sopenharmony_cistatic struct device_attribute format_attr_##_var =			\
1358c2ecf20Sopenharmony_ci	__ATTR(_name, 0444, __tx2_pmu_##_var##_show, NULL)
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ciTX2_PMU_FORMAT_ATTR(event, event, "config:0-4");
1388c2ecf20Sopenharmony_ciTX2_PMU_FORMAT_ATTR(event_ccpi2, event, "config:0-9");
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic struct attribute *l3c_pmu_format_attrs[] = {
1418c2ecf20Sopenharmony_ci	&format_attr_event.attr,
1428c2ecf20Sopenharmony_ci	NULL,
1438c2ecf20Sopenharmony_ci};
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic struct attribute *dmc_pmu_format_attrs[] = {
1468c2ecf20Sopenharmony_ci	&format_attr_event.attr,
1478c2ecf20Sopenharmony_ci	NULL,
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic struct attribute *ccpi2_pmu_format_attrs[] = {
1518c2ecf20Sopenharmony_ci	&format_attr_event_ccpi2.attr,
1528c2ecf20Sopenharmony_ci	NULL,
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic const struct attribute_group l3c_pmu_format_attr_group = {
1568c2ecf20Sopenharmony_ci	.name = "format",
1578c2ecf20Sopenharmony_ci	.attrs = l3c_pmu_format_attrs,
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic const struct attribute_group dmc_pmu_format_attr_group = {
1618c2ecf20Sopenharmony_ci	.name = "format",
1628c2ecf20Sopenharmony_ci	.attrs = dmc_pmu_format_attrs,
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic const struct attribute_group ccpi2_pmu_format_attr_group = {
1668c2ecf20Sopenharmony_ci	.name = "format",
1678c2ecf20Sopenharmony_ci	.attrs = ccpi2_pmu_format_attrs,
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/*
1718c2ecf20Sopenharmony_ci * sysfs event attributes
1728c2ecf20Sopenharmony_ci */
1738c2ecf20Sopenharmony_cistatic ssize_t tx2_pmu_event_show(struct device *dev,
1748c2ecf20Sopenharmony_ci				    struct device_attribute *attr, char *buf)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	struct dev_ext_attribute *eattr;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	eattr = container_of(attr, struct dev_ext_attribute, attr);
1798c2ecf20Sopenharmony_ci	return sprintf(buf, "event=0x%lx\n", (unsigned long) eattr->var);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci#define TX2_EVENT_ATTR(name, config) \
1838c2ecf20Sopenharmony_ci	PMU_EVENT_ATTR(name, tx2_pmu_event_attr_##name, \
1848c2ecf20Sopenharmony_ci			config, tx2_pmu_event_show)
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(read_request, L3_EVENT_READ_REQ);
1878c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(writeback_request, L3_EVENT_WRITEBACK_REQ);
1888c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(inv_nwrite_request, L3_EVENT_INV_N_WRITE_REQ);
1898c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(inv_request, L3_EVENT_INV_REQ);
1908c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(evict_request, L3_EVENT_EVICT_REQ);
1918c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(inv_nwrite_hit, L3_EVENT_INV_N_WRITE_HIT);
1928c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(inv_hit, L3_EVENT_INV_HIT);
1938c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(read_hit, L3_EVENT_READ_HIT);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic struct attribute *l3c_pmu_events_attrs[] = {
1968c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_read_request.attr.attr,
1978c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_writeback_request.attr.attr,
1988c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_inv_nwrite_request.attr.attr,
1998c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_inv_request.attr.attr,
2008c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_evict_request.attr.attr,
2018c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_inv_nwrite_hit.attr.attr,
2028c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_inv_hit.attr.attr,
2038c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_read_hit.attr.attr,
2048c2ecf20Sopenharmony_ci	NULL,
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(cnt_cycles, DMC_EVENT_COUNT_CYCLES);
2088c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(write_txns, DMC_EVENT_WRITE_TXNS);
2098c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(data_transfers, DMC_EVENT_DATA_TRANSFERS);
2108c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(read_txns, DMC_EVENT_READ_TXNS);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic struct attribute *dmc_pmu_events_attrs[] = {
2138c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_cnt_cycles.attr.attr,
2148c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_write_txns.attr.attr,
2158c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_data_transfers.attr.attr,
2168c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_read_txns.attr.attr,
2178c2ecf20Sopenharmony_ci	NULL,
2188c2ecf20Sopenharmony_ci};
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(req_pktsent, CCPI2_EVENT_REQ_PKT_SENT);
2218c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(snoop_pktsent, CCPI2_EVENT_SNOOP_PKT_SENT);
2228c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(data_pktsent, CCPI2_EVENT_DATA_PKT_SENT);
2238c2ecf20Sopenharmony_ciTX2_EVENT_ATTR(gic_pktsent, CCPI2_EVENT_GIC_PKT_SENT);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic struct attribute *ccpi2_pmu_events_attrs[] = {
2268c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_req_pktsent.attr.attr,
2278c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_snoop_pktsent.attr.attr,
2288c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_data_pktsent.attr.attr,
2298c2ecf20Sopenharmony_ci	&tx2_pmu_event_attr_gic_pktsent.attr.attr,
2308c2ecf20Sopenharmony_ci	NULL,
2318c2ecf20Sopenharmony_ci};
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic const struct attribute_group l3c_pmu_events_attr_group = {
2348c2ecf20Sopenharmony_ci	.name = "events",
2358c2ecf20Sopenharmony_ci	.attrs = l3c_pmu_events_attrs,
2368c2ecf20Sopenharmony_ci};
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic const struct attribute_group dmc_pmu_events_attr_group = {
2398c2ecf20Sopenharmony_ci	.name = "events",
2408c2ecf20Sopenharmony_ci	.attrs = dmc_pmu_events_attrs,
2418c2ecf20Sopenharmony_ci};
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic const struct attribute_group ccpi2_pmu_events_attr_group = {
2448c2ecf20Sopenharmony_ci	.name = "events",
2458c2ecf20Sopenharmony_ci	.attrs = ccpi2_pmu_events_attrs,
2468c2ecf20Sopenharmony_ci};
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci/*
2498c2ecf20Sopenharmony_ci * sysfs cpumask attributes
2508c2ecf20Sopenharmony_ci */
2518c2ecf20Sopenharmony_cistatic ssize_t cpumask_show(struct device *dev, struct device_attribute *attr,
2528c2ecf20Sopenharmony_ci		char *buf)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(dev_get_drvdata(dev));
2578c2ecf20Sopenharmony_ci	return cpumap_print_to_pagebuf(true, buf, cpumask_of(tx2_pmu->cpu));
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(cpumask);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic struct attribute *tx2_pmu_cpumask_attrs[] = {
2628c2ecf20Sopenharmony_ci	&dev_attr_cpumask.attr,
2638c2ecf20Sopenharmony_ci	NULL,
2648c2ecf20Sopenharmony_ci};
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic const struct attribute_group pmu_cpumask_attr_group = {
2678c2ecf20Sopenharmony_ci	.attrs = tx2_pmu_cpumask_attrs,
2688c2ecf20Sopenharmony_ci};
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci/*
2718c2ecf20Sopenharmony_ci * Per PMU device attribute groups
2728c2ecf20Sopenharmony_ci */
2738c2ecf20Sopenharmony_cistatic const struct attribute_group *l3c_pmu_attr_groups[] = {
2748c2ecf20Sopenharmony_ci	&l3c_pmu_format_attr_group,
2758c2ecf20Sopenharmony_ci	&pmu_cpumask_attr_group,
2768c2ecf20Sopenharmony_ci	&l3c_pmu_events_attr_group,
2778c2ecf20Sopenharmony_ci	NULL
2788c2ecf20Sopenharmony_ci};
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic const struct attribute_group *dmc_pmu_attr_groups[] = {
2818c2ecf20Sopenharmony_ci	&dmc_pmu_format_attr_group,
2828c2ecf20Sopenharmony_ci	&pmu_cpumask_attr_group,
2838c2ecf20Sopenharmony_ci	&dmc_pmu_events_attr_group,
2848c2ecf20Sopenharmony_ci	NULL
2858c2ecf20Sopenharmony_ci};
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic const struct attribute_group *ccpi2_pmu_attr_groups[] = {
2888c2ecf20Sopenharmony_ci	&ccpi2_pmu_format_attr_group,
2898c2ecf20Sopenharmony_ci	&pmu_cpumask_attr_group,
2908c2ecf20Sopenharmony_ci	&ccpi2_pmu_events_attr_group,
2918c2ecf20Sopenharmony_ci	NULL
2928c2ecf20Sopenharmony_ci};
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic inline u32 reg_readl(unsigned long addr)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	return readl((void __iomem *)addr);
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic inline void reg_writel(u32 val, unsigned long addr)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	writel(val, (void __iomem *)addr);
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic int alloc_counter(struct tx2_uncore_pmu *tx2_pmu)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	int counter;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	counter = find_first_zero_bit(tx2_pmu->active_counters,
3098c2ecf20Sopenharmony_ci				tx2_pmu->max_counters);
3108c2ecf20Sopenharmony_ci	if (counter == tx2_pmu->max_counters)
3118c2ecf20Sopenharmony_ci		return -ENOSPC;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	set_bit(counter, tx2_pmu->active_counters);
3148c2ecf20Sopenharmony_ci	return counter;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic inline void free_counter(struct tx2_uncore_pmu *tx2_pmu, int counter)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	clear_bit(counter, tx2_pmu->active_counters);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic void init_cntr_base_l3c(struct perf_event *event,
3238c2ecf20Sopenharmony_ci		struct tx2_uncore_pmu *tx2_pmu)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
3268c2ecf20Sopenharmony_ci	u32 cmask;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
3298c2ecf20Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* counter ctrl/data reg offset at 8 */
3328c2ecf20Sopenharmony_ci	hwc->config_base = (unsigned long)tx2_pmu->base
3338c2ecf20Sopenharmony_ci		+ L3C_COUNTER_CTL + (8 * GET_COUNTERID(event, cmask));
3348c2ecf20Sopenharmony_ci	hwc->event_base =  (unsigned long)tx2_pmu->base
3358c2ecf20Sopenharmony_ci		+ L3C_COUNTER_DATA + (8 * GET_COUNTERID(event, cmask));
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic void init_cntr_base_dmc(struct perf_event *event,
3398c2ecf20Sopenharmony_ci		struct tx2_uncore_pmu *tx2_pmu)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
3428c2ecf20Sopenharmony_ci	u32 cmask;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
3458c2ecf20Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	hwc->config_base = (unsigned long)tx2_pmu->base
3488c2ecf20Sopenharmony_ci		+ DMC_COUNTER_CTL;
3498c2ecf20Sopenharmony_ci	/* counter data reg offset at 0xc */
3508c2ecf20Sopenharmony_ci	hwc->event_base = (unsigned long)tx2_pmu->base
3518c2ecf20Sopenharmony_ci		+ DMC_COUNTER_DATA + (0xc * GET_COUNTERID(event, cmask));
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic void init_cntr_base_ccpi2(struct perf_event *event,
3558c2ecf20Sopenharmony_ci		struct tx2_uncore_pmu *tx2_pmu)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
3588c2ecf20Sopenharmony_ci	u32 cmask;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	hwc->config_base = (unsigned long)tx2_pmu->base
3638c2ecf20Sopenharmony_ci		+ CCPI2_COUNTER_CTL + (4 * GET_COUNTERID(event, cmask));
3648c2ecf20Sopenharmony_ci	hwc->event_base =  (unsigned long)tx2_pmu->base;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic void uncore_start_event_l3c(struct perf_event *event, int flags)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	u32 val, emask;
3708c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
3718c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
3748c2ecf20Sopenharmony_ci	emask = tx2_pmu->events_mask;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* event id encoded in bits [07:03] */
3778c2ecf20Sopenharmony_ci	val = GET_EVENTID(event, emask) << 3;
3788c2ecf20Sopenharmony_ci	reg_writel(val, hwc->config_base);
3798c2ecf20Sopenharmony_ci	local64_set(&hwc->prev_count, 0);
3808c2ecf20Sopenharmony_ci	reg_writel(0, hwc->event_base);
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic inline void uncore_stop_event_l3c(struct perf_event *event)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	reg_writel(0, event->hw.config_base);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic void uncore_start_event_dmc(struct perf_event *event, int flags)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	u32 val, cmask, emask;
3918c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
3928c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
3938c2ecf20Sopenharmony_ci	int idx, event_id;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
3968c2ecf20Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
3978c2ecf20Sopenharmony_ci	emask = tx2_pmu->events_mask;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	idx = GET_COUNTERID(event, cmask);
4008c2ecf20Sopenharmony_ci	event_id = GET_EVENTID(event, emask);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	/* enable and start counters.
4038c2ecf20Sopenharmony_ci	 * 8 bits for each counter, bits[05:01] of a counter to set event type.
4048c2ecf20Sopenharmony_ci	 */
4058c2ecf20Sopenharmony_ci	val = reg_readl(hwc->config_base);
4068c2ecf20Sopenharmony_ci	val &= ~DMC_EVENT_CFG(idx, 0x1f);
4078c2ecf20Sopenharmony_ci	val |= DMC_EVENT_CFG(idx, event_id);
4088c2ecf20Sopenharmony_ci	reg_writel(val, hwc->config_base);
4098c2ecf20Sopenharmony_ci	local64_set(&hwc->prev_count, 0);
4108c2ecf20Sopenharmony_ci	reg_writel(0, hwc->event_base);
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic void uncore_stop_event_dmc(struct perf_event *event)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	u32 val, cmask;
4168c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
4178c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
4188c2ecf20Sopenharmony_ci	int idx;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
4218c2ecf20Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
4228c2ecf20Sopenharmony_ci	idx = GET_COUNTERID(event, cmask);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/* clear event type(bits[05:01]) to stop counter */
4258c2ecf20Sopenharmony_ci	val = reg_readl(hwc->config_base);
4268c2ecf20Sopenharmony_ci	val &= ~DMC_EVENT_CFG(idx, 0x1f);
4278c2ecf20Sopenharmony_ci	reg_writel(val, hwc->config_base);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic void uncore_start_event_ccpi2(struct perf_event *event, int flags)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	u32 emask;
4338c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
4348c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
4378c2ecf20Sopenharmony_ci	emask = tx2_pmu->events_mask;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* Bit [09:00] to set event id.
4408c2ecf20Sopenharmony_ci	 * Bits [10], set level to rising edge.
4418c2ecf20Sopenharmony_ci	 * Bits [11], set type to edge sensitive.
4428c2ecf20Sopenharmony_ci	 */
4438c2ecf20Sopenharmony_ci	reg_writel((CCPI2_EVENT_TYPE_EDGE_SENSITIVE |
4448c2ecf20Sopenharmony_ci			CCPI2_EVENT_LEVEL_RISING_EDGE |
4458c2ecf20Sopenharmony_ci			GET_EVENTID(event, emask)), hwc->config_base);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	/* reset[4], enable[0] and start[1] counters */
4488c2ecf20Sopenharmony_ci	reg_writel(CCPI2_PERF_CTL_RESET |
4498c2ecf20Sopenharmony_ci			CCPI2_PERF_CTL_START |
4508c2ecf20Sopenharmony_ci			CCPI2_PERF_CTL_ENABLE,
4518c2ecf20Sopenharmony_ci			hwc->event_base + CCPI2_PERF_CTL);
4528c2ecf20Sopenharmony_ci	local64_set(&event->hw.prev_count, 0ULL);
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic void uncore_stop_event_ccpi2(struct perf_event *event)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/* disable and stop counter */
4608c2ecf20Sopenharmony_ci	reg_writel(0, hwc->event_base + CCPI2_PERF_CTL);
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic void tx2_uncore_event_update(struct perf_event *event)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	u64 prev, delta, new = 0;
4668c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
4678c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
4688c2ecf20Sopenharmony_ci	enum tx2_uncore_type type;
4698c2ecf20Sopenharmony_ci	u32 prorate_factor;
4708c2ecf20Sopenharmony_ci	u32 cmask, emask;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
4738c2ecf20Sopenharmony_ci	type = tx2_pmu->type;
4748c2ecf20Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
4758c2ecf20Sopenharmony_ci	emask = tx2_pmu->events_mask;
4768c2ecf20Sopenharmony_ci	prorate_factor = tx2_pmu->prorate_factor;
4778c2ecf20Sopenharmony_ci	if (type == PMU_TYPE_CCPI2) {
4788c2ecf20Sopenharmony_ci		reg_writel(CCPI2_COUNTER_OFFSET +
4798c2ecf20Sopenharmony_ci				GET_COUNTERID(event, cmask),
4808c2ecf20Sopenharmony_ci				hwc->event_base + CCPI2_COUNTER_SEL);
4818c2ecf20Sopenharmony_ci		new = reg_readl(hwc->event_base + CCPI2_COUNTER_DATA_H);
4828c2ecf20Sopenharmony_ci		new = (new << 32) +
4838c2ecf20Sopenharmony_ci			reg_readl(hwc->event_base + CCPI2_COUNTER_DATA_L);
4848c2ecf20Sopenharmony_ci		prev = local64_xchg(&hwc->prev_count, new);
4858c2ecf20Sopenharmony_ci		delta = new - prev;
4868c2ecf20Sopenharmony_ci	} else {
4878c2ecf20Sopenharmony_ci		new = reg_readl(hwc->event_base);
4888c2ecf20Sopenharmony_ci		prev = local64_xchg(&hwc->prev_count, new);
4898c2ecf20Sopenharmony_ci		/* handles rollover of 32 bit counter */
4908c2ecf20Sopenharmony_ci		delta = (u32)(((1UL << 32) - prev) + new);
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* DMC event data_transfers granularity is 16 Bytes, convert it to 64 */
4948c2ecf20Sopenharmony_ci	if (type == PMU_TYPE_DMC &&
4958c2ecf20Sopenharmony_ci			GET_EVENTID(event, emask) == DMC_EVENT_DATA_TRANSFERS)
4968c2ecf20Sopenharmony_ci		delta = delta/4;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	/* L3C and DMC has 16 and 8 interleave channels respectively.
4998c2ecf20Sopenharmony_ci	 * The sampled value is for channel 0 and multiplied with
5008c2ecf20Sopenharmony_ci	 * prorate_factor to get the count for a device.
5018c2ecf20Sopenharmony_ci	 */
5028c2ecf20Sopenharmony_ci	local64_add(delta * prorate_factor, &event->count);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic enum tx2_uncore_type get_tx2_pmu_type(struct acpi_device *adev)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	int i = 0;
5088c2ecf20Sopenharmony_ci	struct acpi_tx2_pmu_device {
5098c2ecf20Sopenharmony_ci		__u8 id[ACPI_ID_LEN];
5108c2ecf20Sopenharmony_ci		enum tx2_uncore_type type;
5118c2ecf20Sopenharmony_ci	} devices[] = {
5128c2ecf20Sopenharmony_ci		{"CAV901D", PMU_TYPE_L3C},
5138c2ecf20Sopenharmony_ci		{"CAV901F", PMU_TYPE_DMC},
5148c2ecf20Sopenharmony_ci		{"CAV901E", PMU_TYPE_CCPI2},
5158c2ecf20Sopenharmony_ci		{"", PMU_TYPE_INVALID}
5168c2ecf20Sopenharmony_ci	};
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	while (devices[i].type != PMU_TYPE_INVALID) {
5198c2ecf20Sopenharmony_ci		if (!strcmp(acpi_device_hid(adev), devices[i].id))
5208c2ecf20Sopenharmony_ci			break;
5218c2ecf20Sopenharmony_ci		i++;
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	return devices[i].type;
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic bool tx2_uncore_validate_event(struct pmu *pmu,
5288c2ecf20Sopenharmony_ci				  struct perf_event *event, int *counters)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	if (is_software_event(event))
5318c2ecf20Sopenharmony_ci		return true;
5328c2ecf20Sopenharmony_ci	/* Reject groups spanning multiple HW PMUs. */
5338c2ecf20Sopenharmony_ci	if (event->pmu != pmu)
5348c2ecf20Sopenharmony_ci		return false;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	*counters = *counters + 1;
5378c2ecf20Sopenharmony_ci	return true;
5388c2ecf20Sopenharmony_ci}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci/*
5418c2ecf20Sopenharmony_ci * Make sure the group of events can be scheduled at once
5428c2ecf20Sopenharmony_ci * on the PMU.
5438c2ecf20Sopenharmony_ci */
5448c2ecf20Sopenharmony_cistatic bool tx2_uncore_validate_event_group(struct perf_event *event,
5458c2ecf20Sopenharmony_ci		int max_counters)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	struct perf_event *sibling, *leader = event->group_leader;
5488c2ecf20Sopenharmony_ci	int counters = 0;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (event->group_leader == event)
5518c2ecf20Sopenharmony_ci		return true;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	if (!tx2_uncore_validate_event(event->pmu, leader, &counters))
5548c2ecf20Sopenharmony_ci		return false;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	for_each_sibling_event(sibling, leader) {
5578c2ecf20Sopenharmony_ci		if (!tx2_uncore_validate_event(event->pmu, sibling, &counters))
5588c2ecf20Sopenharmony_ci			return false;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	if (!tx2_uncore_validate_event(event->pmu, event, &counters))
5628c2ecf20Sopenharmony_ci		return false;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	/*
5658c2ecf20Sopenharmony_ci	 * If the group requires more counters than the HW has,
5668c2ecf20Sopenharmony_ci	 * it cannot ever be scheduled.
5678c2ecf20Sopenharmony_ci	 */
5688c2ecf20Sopenharmony_ci	return counters <= max_counters;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic int tx2_uncore_event_init(struct perf_event *event)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
5758c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	/* Test the event attr type check for PMU enumeration */
5788c2ecf20Sopenharmony_ci	if (event->attr.type != event->pmu->type)
5798c2ecf20Sopenharmony_ci		return -ENOENT;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	/*
5828c2ecf20Sopenharmony_ci	 * SOC PMU counters are shared across all cores.
5838c2ecf20Sopenharmony_ci	 * Therefore, it does not support per-process mode.
5848c2ecf20Sopenharmony_ci	 * Also, it does not support event sampling mode.
5858c2ecf20Sopenharmony_ci	 */
5868c2ecf20Sopenharmony_ci	if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
5878c2ecf20Sopenharmony_ci		return -EINVAL;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	if (event->cpu < 0)
5908c2ecf20Sopenharmony_ci		return -EINVAL;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
5938c2ecf20Sopenharmony_ci	if (tx2_pmu->cpu >= nr_cpu_ids)
5948c2ecf20Sopenharmony_ci		return -EINVAL;
5958c2ecf20Sopenharmony_ci	event->cpu = tx2_pmu->cpu;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	if (event->attr.config >= tx2_pmu->max_events)
5988c2ecf20Sopenharmony_ci		return -EINVAL;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	/* store event id */
6018c2ecf20Sopenharmony_ci	hwc->config = event->attr.config;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	/* Validate the group */
6048c2ecf20Sopenharmony_ci	if (!tx2_uncore_validate_event_group(event, tx2_pmu->max_counters))
6058c2ecf20Sopenharmony_ci		return -EINVAL;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	return 0;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic void tx2_uncore_event_start(struct perf_event *event, int flags)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
6138c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	hwc->state = 0;
6168c2ecf20Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	tx2_pmu->start_event(event, flags);
6198c2ecf20Sopenharmony_ci	perf_event_update_userpage(event);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	/* No hrtimer needed for CCPI2, 64-bit counters */
6228c2ecf20Sopenharmony_ci	if (!tx2_pmu->hrtimer_callback)
6238c2ecf20Sopenharmony_ci		return;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	/* Start timer for first event */
6268c2ecf20Sopenharmony_ci	if (bitmap_weight(tx2_pmu->active_counters,
6278c2ecf20Sopenharmony_ci				tx2_pmu->max_counters) == 1) {
6288c2ecf20Sopenharmony_ci		hrtimer_start(&tx2_pmu->hrtimer,
6298c2ecf20Sopenharmony_ci			ns_to_ktime(tx2_pmu->hrtimer_interval),
6308c2ecf20Sopenharmony_ci			HRTIMER_MODE_REL_PINNED);
6318c2ecf20Sopenharmony_ci	}
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic void tx2_uncore_event_stop(struct perf_event *event, int flags)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
6378c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (hwc->state & PERF_HES_UPTODATE)
6408c2ecf20Sopenharmony_ci		return;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
6438c2ecf20Sopenharmony_ci	tx2_pmu->stop_event(event);
6448c2ecf20Sopenharmony_ci	WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
6458c2ecf20Sopenharmony_ci	hwc->state |= PERF_HES_STOPPED;
6468c2ecf20Sopenharmony_ci	if (flags & PERF_EF_UPDATE) {
6478c2ecf20Sopenharmony_ci		tx2_uncore_event_update(event);
6488c2ecf20Sopenharmony_ci		hwc->state |= PERF_HES_UPTODATE;
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic int tx2_uncore_event_add(struct perf_event *event, int flags)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
6558c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	tx2_pmu = pmu_to_tx2_pmu(event->pmu);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	/* Allocate a free counter */
6608c2ecf20Sopenharmony_ci	hwc->idx  = alloc_counter(tx2_pmu);
6618c2ecf20Sopenharmony_ci	if (hwc->idx < 0)
6628c2ecf20Sopenharmony_ci		return -EAGAIN;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	tx2_pmu->events[hwc->idx] = event;
6658c2ecf20Sopenharmony_ci	/* set counter control and data registers base address */
6668c2ecf20Sopenharmony_ci	tx2_pmu->init_cntr_base(event, tx2_pmu);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
6698c2ecf20Sopenharmony_ci	if (flags & PERF_EF_START)
6708c2ecf20Sopenharmony_ci		tx2_uncore_event_start(event, flags);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	return 0;
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_cistatic void tx2_uncore_event_del(struct perf_event *event, int flags)
6768c2ecf20Sopenharmony_ci{
6778c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu = pmu_to_tx2_pmu(event->pmu);
6788c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
6798c2ecf20Sopenharmony_ci	u32 cmask;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	cmask = tx2_pmu->counters_mask;
6828c2ecf20Sopenharmony_ci	tx2_uncore_event_stop(event, PERF_EF_UPDATE);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	/* clear the assigned counter */
6858c2ecf20Sopenharmony_ci	free_counter(tx2_pmu, GET_COUNTERID(event, cmask));
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	perf_event_update_userpage(event);
6888c2ecf20Sopenharmony_ci	tx2_pmu->events[hwc->idx] = NULL;
6898c2ecf20Sopenharmony_ci	hwc->idx = -1;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (!tx2_pmu->hrtimer_callback)
6928c2ecf20Sopenharmony_ci		return;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	if (bitmap_empty(tx2_pmu->active_counters, tx2_pmu->max_counters))
6958c2ecf20Sopenharmony_ci		hrtimer_cancel(&tx2_pmu->hrtimer);
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic void tx2_uncore_event_read(struct perf_event *event)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	tx2_uncore_event_update(event);
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_cistatic enum hrtimer_restart tx2_hrtimer_callback(struct hrtimer *timer)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
7068c2ecf20Sopenharmony_ci	int max_counters, idx;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	tx2_pmu = container_of(timer, struct tx2_uncore_pmu, hrtimer);
7098c2ecf20Sopenharmony_ci	max_counters = tx2_pmu->max_counters;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (bitmap_empty(tx2_pmu->active_counters, max_counters))
7128c2ecf20Sopenharmony_ci		return HRTIMER_NORESTART;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	for_each_set_bit(idx, tx2_pmu->active_counters, max_counters) {
7158c2ecf20Sopenharmony_ci		struct perf_event *event = tx2_pmu->events[idx];
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		tx2_uncore_event_update(event);
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci	hrtimer_forward_now(timer, ns_to_ktime(tx2_pmu->hrtimer_interval));
7208c2ecf20Sopenharmony_ci	return HRTIMER_RESTART;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_cistatic int tx2_uncore_pmu_register(
7248c2ecf20Sopenharmony_ci		struct tx2_uncore_pmu *tx2_pmu)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	struct device *dev = tx2_pmu->dev;
7278c2ecf20Sopenharmony_ci	char *name = tx2_pmu->name;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	/* Perf event registration */
7308c2ecf20Sopenharmony_ci	tx2_pmu->pmu = (struct pmu) {
7318c2ecf20Sopenharmony_ci		.module         = THIS_MODULE,
7328c2ecf20Sopenharmony_ci		.attr_groups	= tx2_pmu->attr_groups,
7338c2ecf20Sopenharmony_ci		.task_ctx_nr	= perf_invalid_context,
7348c2ecf20Sopenharmony_ci		.event_init	= tx2_uncore_event_init,
7358c2ecf20Sopenharmony_ci		.add		= tx2_uncore_event_add,
7368c2ecf20Sopenharmony_ci		.del		= tx2_uncore_event_del,
7378c2ecf20Sopenharmony_ci		.start		= tx2_uncore_event_start,
7388c2ecf20Sopenharmony_ci		.stop		= tx2_uncore_event_stop,
7398c2ecf20Sopenharmony_ci		.read		= tx2_uncore_event_read,
7408c2ecf20Sopenharmony_ci		.capabilities	= PERF_PMU_CAP_NO_EXCLUDE,
7418c2ecf20Sopenharmony_ci	};
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	tx2_pmu->pmu.name = devm_kasprintf(dev, GFP_KERNEL,
7448c2ecf20Sopenharmony_ci			"%s", name);
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	return perf_pmu_register(&tx2_pmu->pmu, tx2_pmu->pmu.name, -1);
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistatic int tx2_uncore_pmu_add_dev(struct tx2_uncore_pmu *tx2_pmu)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	int ret, cpu;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	cpu = cpumask_any_and(cpumask_of_node(tx2_pmu->node),
7548c2ecf20Sopenharmony_ci			cpu_online_mask);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	tx2_pmu->cpu = cpu;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	if (tx2_pmu->hrtimer_callback) {
7598c2ecf20Sopenharmony_ci		hrtimer_init(&tx2_pmu->hrtimer,
7608c2ecf20Sopenharmony_ci				CLOCK_MONOTONIC, HRTIMER_MODE_REL);
7618c2ecf20Sopenharmony_ci		tx2_pmu->hrtimer.function = tx2_pmu->hrtimer_callback;
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	ret = tx2_uncore_pmu_register(tx2_pmu);
7658c2ecf20Sopenharmony_ci	if (ret) {
7668c2ecf20Sopenharmony_ci		dev_err(tx2_pmu->dev, "%s PMU: Failed to init driver\n",
7678c2ecf20Sopenharmony_ci				tx2_pmu->name);
7688c2ecf20Sopenharmony_ci		return -ENODEV;
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	/* register hotplug callback for the pmu */
7728c2ecf20Sopenharmony_ci	ret = cpuhp_state_add_instance(
7738c2ecf20Sopenharmony_ci			CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE,
7748c2ecf20Sopenharmony_ci			&tx2_pmu->hpnode);
7758c2ecf20Sopenharmony_ci	if (ret) {
7768c2ecf20Sopenharmony_ci		dev_err(tx2_pmu->dev, "Error %d registering hotplug", ret);
7778c2ecf20Sopenharmony_ci		return ret;
7788c2ecf20Sopenharmony_ci	}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	/* Add to list */
7818c2ecf20Sopenharmony_ci	list_add(&tx2_pmu->entry, &tx2_pmus);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	dev_dbg(tx2_pmu->dev, "%s PMU UNCORE registered\n",
7848c2ecf20Sopenharmony_ci			tx2_pmu->pmu.name);
7858c2ecf20Sopenharmony_ci	return ret;
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cistatic struct tx2_uncore_pmu *tx2_uncore_pmu_init_dev(struct device *dev,
7898c2ecf20Sopenharmony_ci		acpi_handle handle, struct acpi_device *adev, u32 type)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
7928c2ecf20Sopenharmony_ci	void __iomem *base;
7938c2ecf20Sopenharmony_ci	struct resource res;
7948c2ecf20Sopenharmony_ci	struct resource_entry *rentry;
7958c2ecf20Sopenharmony_ci	struct list_head list;
7968c2ecf20Sopenharmony_ci	int ret;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&list);
7998c2ecf20Sopenharmony_ci	ret = acpi_dev_get_resources(adev, &list, NULL, NULL);
8008c2ecf20Sopenharmony_ci	if (ret <= 0) {
8018c2ecf20Sopenharmony_ci		dev_err(dev, "failed to parse _CRS method, error %d\n", ret);
8028c2ecf20Sopenharmony_ci		return NULL;
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	list_for_each_entry(rentry, &list, node) {
8068c2ecf20Sopenharmony_ci		if (resource_type(rentry->res) == IORESOURCE_MEM) {
8078c2ecf20Sopenharmony_ci			res = *rentry->res;
8088c2ecf20Sopenharmony_ci			rentry = NULL;
8098c2ecf20Sopenharmony_ci			break;
8108c2ecf20Sopenharmony_ci		}
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci	acpi_dev_free_resource_list(&list);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	if (rentry) {
8158c2ecf20Sopenharmony_ci		dev_err(dev, "PMU type %d: Fail to find resource\n", type);
8168c2ecf20Sopenharmony_ci		return NULL;
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	base = devm_ioremap_resource(dev, &res);
8208c2ecf20Sopenharmony_ci	if (IS_ERR(base)) {
8218c2ecf20Sopenharmony_ci		dev_err(dev, "PMU type %d: Fail to map resource\n", type);
8228c2ecf20Sopenharmony_ci		return NULL;
8238c2ecf20Sopenharmony_ci	}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	tx2_pmu = devm_kzalloc(dev, sizeof(*tx2_pmu), GFP_KERNEL);
8268c2ecf20Sopenharmony_ci	if (!tx2_pmu)
8278c2ecf20Sopenharmony_ci		return NULL;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	tx2_pmu->dev = dev;
8308c2ecf20Sopenharmony_ci	tx2_pmu->type = type;
8318c2ecf20Sopenharmony_ci	tx2_pmu->base = base;
8328c2ecf20Sopenharmony_ci	tx2_pmu->node = dev_to_node(dev);
8338c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tx2_pmu->entry);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	switch (tx2_pmu->type) {
8368c2ecf20Sopenharmony_ci	case PMU_TYPE_L3C:
8378c2ecf20Sopenharmony_ci		tx2_pmu->max_counters = TX2_PMU_DMC_L3C_MAX_COUNTERS;
8388c2ecf20Sopenharmony_ci		tx2_pmu->counters_mask = 0x3;
8398c2ecf20Sopenharmony_ci		tx2_pmu->prorate_factor = TX2_PMU_L3_TILES;
8408c2ecf20Sopenharmony_ci		tx2_pmu->max_events = L3_EVENT_MAX;
8418c2ecf20Sopenharmony_ci		tx2_pmu->events_mask = 0x1f;
8428c2ecf20Sopenharmony_ci		tx2_pmu->hrtimer_interval = TX2_PMU_HRTIMER_INTERVAL;
8438c2ecf20Sopenharmony_ci		tx2_pmu->hrtimer_callback = tx2_hrtimer_callback;
8448c2ecf20Sopenharmony_ci		tx2_pmu->attr_groups = l3c_pmu_attr_groups;
8458c2ecf20Sopenharmony_ci		tx2_pmu->name = devm_kasprintf(dev, GFP_KERNEL,
8468c2ecf20Sopenharmony_ci				"uncore_l3c_%d", tx2_pmu->node);
8478c2ecf20Sopenharmony_ci		tx2_pmu->init_cntr_base = init_cntr_base_l3c;
8488c2ecf20Sopenharmony_ci		tx2_pmu->start_event = uncore_start_event_l3c;
8498c2ecf20Sopenharmony_ci		tx2_pmu->stop_event = uncore_stop_event_l3c;
8508c2ecf20Sopenharmony_ci		break;
8518c2ecf20Sopenharmony_ci	case PMU_TYPE_DMC:
8528c2ecf20Sopenharmony_ci		tx2_pmu->max_counters = TX2_PMU_DMC_L3C_MAX_COUNTERS;
8538c2ecf20Sopenharmony_ci		tx2_pmu->counters_mask = 0x3;
8548c2ecf20Sopenharmony_ci		tx2_pmu->prorate_factor = TX2_PMU_DMC_CHANNELS;
8558c2ecf20Sopenharmony_ci		tx2_pmu->max_events = DMC_EVENT_MAX;
8568c2ecf20Sopenharmony_ci		tx2_pmu->events_mask = 0x1f;
8578c2ecf20Sopenharmony_ci		tx2_pmu->hrtimer_interval = TX2_PMU_HRTIMER_INTERVAL;
8588c2ecf20Sopenharmony_ci		tx2_pmu->hrtimer_callback = tx2_hrtimer_callback;
8598c2ecf20Sopenharmony_ci		tx2_pmu->attr_groups = dmc_pmu_attr_groups;
8608c2ecf20Sopenharmony_ci		tx2_pmu->name = devm_kasprintf(dev, GFP_KERNEL,
8618c2ecf20Sopenharmony_ci				"uncore_dmc_%d", tx2_pmu->node);
8628c2ecf20Sopenharmony_ci		tx2_pmu->init_cntr_base = init_cntr_base_dmc;
8638c2ecf20Sopenharmony_ci		tx2_pmu->start_event = uncore_start_event_dmc;
8648c2ecf20Sopenharmony_ci		tx2_pmu->stop_event = uncore_stop_event_dmc;
8658c2ecf20Sopenharmony_ci		break;
8668c2ecf20Sopenharmony_ci	case PMU_TYPE_CCPI2:
8678c2ecf20Sopenharmony_ci		/* CCPI2 has 8 counters */
8688c2ecf20Sopenharmony_ci		tx2_pmu->max_counters = TX2_PMU_CCPI2_MAX_COUNTERS;
8698c2ecf20Sopenharmony_ci		tx2_pmu->counters_mask = 0x7;
8708c2ecf20Sopenharmony_ci		tx2_pmu->prorate_factor = 1;
8718c2ecf20Sopenharmony_ci		tx2_pmu->max_events = CCPI2_EVENT_MAX;
8728c2ecf20Sopenharmony_ci		tx2_pmu->events_mask = 0x1ff;
8738c2ecf20Sopenharmony_ci		tx2_pmu->attr_groups = ccpi2_pmu_attr_groups;
8748c2ecf20Sopenharmony_ci		tx2_pmu->name = devm_kasprintf(dev, GFP_KERNEL,
8758c2ecf20Sopenharmony_ci				"uncore_ccpi2_%d", tx2_pmu->node);
8768c2ecf20Sopenharmony_ci		tx2_pmu->init_cntr_base = init_cntr_base_ccpi2;
8778c2ecf20Sopenharmony_ci		tx2_pmu->start_event = uncore_start_event_ccpi2;
8788c2ecf20Sopenharmony_ci		tx2_pmu->stop_event = uncore_stop_event_ccpi2;
8798c2ecf20Sopenharmony_ci		tx2_pmu->hrtimer_callback = NULL;
8808c2ecf20Sopenharmony_ci		break;
8818c2ecf20Sopenharmony_ci	case PMU_TYPE_INVALID:
8828c2ecf20Sopenharmony_ci		devm_kfree(dev, tx2_pmu);
8838c2ecf20Sopenharmony_ci		return NULL;
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	return tx2_pmu;
8878c2ecf20Sopenharmony_ci}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_cistatic acpi_status tx2_uncore_pmu_add(acpi_handle handle, u32 level,
8908c2ecf20Sopenharmony_ci				    void *data, void **return_value)
8918c2ecf20Sopenharmony_ci{
8928c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
8938c2ecf20Sopenharmony_ci	struct acpi_device *adev;
8948c2ecf20Sopenharmony_ci	enum tx2_uncore_type type;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	if (acpi_bus_get_device(handle, &adev))
8978c2ecf20Sopenharmony_ci		return AE_OK;
8988c2ecf20Sopenharmony_ci	if (acpi_bus_get_status(adev) || !adev->status.present)
8998c2ecf20Sopenharmony_ci		return AE_OK;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	type = get_tx2_pmu_type(adev);
9028c2ecf20Sopenharmony_ci	if (type == PMU_TYPE_INVALID)
9038c2ecf20Sopenharmony_ci		return AE_OK;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	tx2_pmu = tx2_uncore_pmu_init_dev((struct device *)data,
9068c2ecf20Sopenharmony_ci			handle, adev, type);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	if (!tx2_pmu)
9098c2ecf20Sopenharmony_ci		return AE_ERROR;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	if (tx2_uncore_pmu_add_dev(tx2_pmu)) {
9128c2ecf20Sopenharmony_ci		/* Can't add the PMU device, abort */
9138c2ecf20Sopenharmony_ci		return AE_ERROR;
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci	return AE_OK;
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cistatic int tx2_uncore_pmu_online_cpu(unsigned int cpu,
9198c2ecf20Sopenharmony_ci		struct hlist_node *hpnode)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	tx2_pmu = hlist_entry_safe(hpnode,
9248c2ecf20Sopenharmony_ci			struct tx2_uncore_pmu, hpnode);
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	/* Pick this CPU, If there is no CPU/PMU association and both are
9278c2ecf20Sopenharmony_ci	 * from same node.
9288c2ecf20Sopenharmony_ci	 */
9298c2ecf20Sopenharmony_ci	if ((tx2_pmu->cpu >= nr_cpu_ids) &&
9308c2ecf20Sopenharmony_ci		(tx2_pmu->node == cpu_to_node(cpu)))
9318c2ecf20Sopenharmony_ci		tx2_pmu->cpu = cpu;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	return 0;
9348c2ecf20Sopenharmony_ci}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_cistatic int tx2_uncore_pmu_offline_cpu(unsigned int cpu,
9378c2ecf20Sopenharmony_ci		struct hlist_node *hpnode)
9388c2ecf20Sopenharmony_ci{
9398c2ecf20Sopenharmony_ci	int new_cpu;
9408c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu;
9418c2ecf20Sopenharmony_ci	struct cpumask cpu_online_mask_temp;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	tx2_pmu = hlist_entry_safe(hpnode,
9448c2ecf20Sopenharmony_ci			struct tx2_uncore_pmu, hpnode);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	if (cpu != tx2_pmu->cpu)
9478c2ecf20Sopenharmony_ci		return 0;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	if (tx2_pmu->hrtimer_callback)
9508c2ecf20Sopenharmony_ci		hrtimer_cancel(&tx2_pmu->hrtimer);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	cpumask_copy(&cpu_online_mask_temp, cpu_online_mask);
9538c2ecf20Sopenharmony_ci	cpumask_clear_cpu(cpu, &cpu_online_mask_temp);
9548c2ecf20Sopenharmony_ci	new_cpu = cpumask_any_and(
9558c2ecf20Sopenharmony_ci			cpumask_of_node(tx2_pmu->node),
9568c2ecf20Sopenharmony_ci			&cpu_online_mask_temp);
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	tx2_pmu->cpu = new_cpu;
9598c2ecf20Sopenharmony_ci	if (new_cpu >= nr_cpu_ids)
9608c2ecf20Sopenharmony_ci		return 0;
9618c2ecf20Sopenharmony_ci	perf_pmu_migrate_context(&tx2_pmu->pmu, cpu, new_cpu);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	return 0;
9648c2ecf20Sopenharmony_ci}
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_cistatic const struct acpi_device_id tx2_uncore_acpi_match[] = {
9678c2ecf20Sopenharmony_ci	{"CAV901C", 0},
9688c2ecf20Sopenharmony_ci	{},
9698c2ecf20Sopenharmony_ci};
9708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, tx2_uncore_acpi_match);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_cistatic int tx2_uncore_probe(struct platform_device *pdev)
9738c2ecf20Sopenharmony_ci{
9748c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
9758c2ecf20Sopenharmony_ci	acpi_handle handle;
9768c2ecf20Sopenharmony_ci	acpi_status status;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	set_dev_node(dev, acpi_get_node(ACPI_HANDLE(dev)));
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	if (!has_acpi_companion(dev))
9818c2ecf20Sopenharmony_ci		return -ENODEV;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	handle = ACPI_HANDLE(dev);
9848c2ecf20Sopenharmony_ci	if (!handle)
9858c2ecf20Sopenharmony_ci		return -EINVAL;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	/* Walk through the tree for all PMU UNCORE devices */
9888c2ecf20Sopenharmony_ci	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
9898c2ecf20Sopenharmony_ci				     tx2_uncore_pmu_add,
9908c2ecf20Sopenharmony_ci				     NULL, dev, NULL);
9918c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
9928c2ecf20Sopenharmony_ci		dev_err(dev, "failed to probe PMU devices\n");
9938c2ecf20Sopenharmony_ci		return_ACPI_STATUS(status);
9948c2ecf20Sopenharmony_ci	}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	dev_info(dev, "node%d: pmu uncore registered\n", dev_to_node(dev));
9978c2ecf20Sopenharmony_ci	return 0;
9988c2ecf20Sopenharmony_ci}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_cistatic int tx2_uncore_remove(struct platform_device *pdev)
10018c2ecf20Sopenharmony_ci{
10028c2ecf20Sopenharmony_ci	struct tx2_uncore_pmu *tx2_pmu, *temp;
10038c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	if (!list_empty(&tx2_pmus)) {
10068c2ecf20Sopenharmony_ci		list_for_each_entry_safe(tx2_pmu, temp, &tx2_pmus, entry) {
10078c2ecf20Sopenharmony_ci			if (tx2_pmu->node == dev_to_node(dev)) {
10088c2ecf20Sopenharmony_ci				cpuhp_state_remove_instance_nocalls(
10098c2ecf20Sopenharmony_ci					CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE,
10108c2ecf20Sopenharmony_ci					&tx2_pmu->hpnode);
10118c2ecf20Sopenharmony_ci				perf_pmu_unregister(&tx2_pmu->pmu);
10128c2ecf20Sopenharmony_ci				list_del(&tx2_pmu->entry);
10138c2ecf20Sopenharmony_ci			}
10148c2ecf20Sopenharmony_ci		}
10158c2ecf20Sopenharmony_ci	}
10168c2ecf20Sopenharmony_ci	return 0;
10178c2ecf20Sopenharmony_ci}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_cistatic struct platform_driver tx2_uncore_driver = {
10208c2ecf20Sopenharmony_ci	.driver = {
10218c2ecf20Sopenharmony_ci		.name		= "tx2-uncore-pmu",
10228c2ecf20Sopenharmony_ci		.acpi_match_table = ACPI_PTR(tx2_uncore_acpi_match),
10238c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
10248c2ecf20Sopenharmony_ci	},
10258c2ecf20Sopenharmony_ci	.probe = tx2_uncore_probe,
10268c2ecf20Sopenharmony_ci	.remove = tx2_uncore_remove,
10278c2ecf20Sopenharmony_ci};
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_cistatic int __init tx2_uncore_driver_init(void)
10308c2ecf20Sopenharmony_ci{
10318c2ecf20Sopenharmony_ci	int ret;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE,
10348c2ecf20Sopenharmony_ci				      "perf/tx2/uncore:online",
10358c2ecf20Sopenharmony_ci				      tx2_uncore_pmu_online_cpu,
10368c2ecf20Sopenharmony_ci				      tx2_uncore_pmu_offline_cpu);
10378c2ecf20Sopenharmony_ci	if (ret) {
10388c2ecf20Sopenharmony_ci		pr_err("TX2 PMU: setup hotplug failed(%d)\n", ret);
10398c2ecf20Sopenharmony_ci		return ret;
10408c2ecf20Sopenharmony_ci	}
10418c2ecf20Sopenharmony_ci	ret = platform_driver_register(&tx2_uncore_driver);
10428c2ecf20Sopenharmony_ci	if (ret)
10438c2ecf20Sopenharmony_ci		cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE);
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	return ret;
10468c2ecf20Sopenharmony_ci}
10478c2ecf20Sopenharmony_cimodule_init(tx2_uncore_driver_init);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_cistatic void __exit tx2_uncore_driver_exit(void)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	platform_driver_unregister(&tx2_uncore_driver);
10528c2ecf20Sopenharmony_ci	cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CAVIUM_TX2_UNCORE_ONLINE);
10538c2ecf20Sopenharmony_ci}
10548c2ecf20Sopenharmony_cimodule_exit(tx2_uncore_driver_exit);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ThunderX2 UNCORE PMU driver");
10578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
10588c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ganapatrao Kulkarni <gkulkarni@cavium.com>");
1059