162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright 2019 Advanced Micro Devices, Inc.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
1262306a36Sopenharmony_ci * all copies or substantial portions of the Software.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1562306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1662306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1762306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1862306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1962306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2062306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/perf_event.h>
2562306a36Sopenharmony_ci#include <linux/init.h>
2662306a36Sopenharmony_ci#include "amdgpu.h"
2762306a36Sopenharmony_ci#include "amdgpu_pmu.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define PMU_NAME_SIZE 32
3062306a36Sopenharmony_ci#define NUM_FORMATS_AMDGPU_PMU		4
3162306a36Sopenharmony_ci#define NUM_FORMATS_DF_VEGA20		3
3262306a36Sopenharmony_ci#define NUM_EVENTS_DF_VEGA20		8
3362306a36Sopenharmony_ci#define NUM_EVENT_TYPES_VEGA20		1
3462306a36Sopenharmony_ci#define NUM_EVENTS_VEGA20_XGMI		2
3562306a36Sopenharmony_ci#define NUM_EVENTS_VEGA20_MAX		NUM_EVENTS_VEGA20_XGMI
3662306a36Sopenharmony_ci#define NUM_EVENT_TYPES_ARCTURUS	1
3762306a36Sopenharmony_ci#define NUM_EVENTS_ARCTURUS_XGMI	6
3862306a36Sopenharmony_ci#define NUM_EVENTS_ARCTURUS_MAX		NUM_EVENTS_ARCTURUS_XGMI
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct amdgpu_pmu_event_attribute {
4162306a36Sopenharmony_ci	struct device_attribute attr;
4262306a36Sopenharmony_ci	const char *event_str;
4362306a36Sopenharmony_ci	unsigned int type;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* record to keep track of pmu entry per pmu type per device */
4762306a36Sopenharmony_cistruct amdgpu_pmu_entry {
4862306a36Sopenharmony_ci	struct list_head entry;
4962306a36Sopenharmony_ci	struct amdgpu_device *adev;
5062306a36Sopenharmony_ci	struct pmu pmu;
5162306a36Sopenharmony_ci	unsigned int pmu_perf_type;
5262306a36Sopenharmony_ci	char *pmu_type_name;
5362306a36Sopenharmony_ci	char *pmu_file_prefix;
5462306a36Sopenharmony_ci	struct attribute_group fmt_attr_group;
5562306a36Sopenharmony_ci	struct amdgpu_pmu_event_attribute *fmt_attr;
5662306a36Sopenharmony_ci	struct attribute_group evt_attr_group;
5762306a36Sopenharmony_ci	struct amdgpu_pmu_event_attribute *evt_attr;
5862306a36Sopenharmony_ci};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic ssize_t amdgpu_pmu_event_show(struct device *dev,
6162306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct amdgpu_pmu_event_attribute *amdgpu_pmu_attr;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	amdgpu_pmu_attr = container_of(attr, struct amdgpu_pmu_event_attribute,
6662306a36Sopenharmony_ci									attr);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (!amdgpu_pmu_attr->type)
6962306a36Sopenharmony_ci		return sprintf(buf, "%s\n", amdgpu_pmu_attr->event_str);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return sprintf(buf, "%s,type=0x%x\n",
7262306a36Sopenharmony_ci			amdgpu_pmu_attr->event_str, amdgpu_pmu_attr->type);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic LIST_HEAD(amdgpu_pmu_list);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistruct amdgpu_pmu_attr {
7962306a36Sopenharmony_ci	const char *name;
8062306a36Sopenharmony_ci	const char *config;
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistruct amdgpu_pmu_type {
8462306a36Sopenharmony_ci	const unsigned int type;
8562306a36Sopenharmony_ci	const unsigned int num_of_type;
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistruct amdgpu_pmu_config {
8962306a36Sopenharmony_ci	struct amdgpu_pmu_attr *formats;
9062306a36Sopenharmony_ci	unsigned int num_formats;
9162306a36Sopenharmony_ci	struct amdgpu_pmu_attr *events;
9262306a36Sopenharmony_ci	unsigned int num_events;
9362306a36Sopenharmony_ci	struct amdgpu_pmu_type *types;
9462306a36Sopenharmony_ci	unsigned int num_types;
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * Events fall under two categories:
9962306a36Sopenharmony_ci *  - PMU typed
10062306a36Sopenharmony_ci *    Events in /sys/bus/event_source/devices/amdgpu_<pmu_type>_<dev_num> have
10162306a36Sopenharmony_ci *    performance counter operations handled by one IP <pmu_type>.  Formats and
10262306a36Sopenharmony_ci *    events should be defined by <pmu_type>_<asic_type>_formats and
10362306a36Sopenharmony_ci *    <pmu_type>_<asic_type>_events respectively.
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci *  - Event config typed
10662306a36Sopenharmony_ci *    Events in /sys/bus/event_source/devices/amdgpu_<dev_num> have performance
10762306a36Sopenharmony_ci *    counter operations that can be handled by multiple IPs dictated by their
10862306a36Sopenharmony_ci *    "type" format field.  Formats and events should be defined by
10962306a36Sopenharmony_ci *    amdgpu_pmu_formats and <asic_type>_events respectively.  Format field
11062306a36Sopenharmony_ci *    "type" is generated in amdgpu_pmu_event_show and defined in
11162306a36Sopenharmony_ci *    <asic_type>_event_config_types.
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic struct amdgpu_pmu_attr amdgpu_pmu_formats[NUM_FORMATS_AMDGPU_PMU] = {
11562306a36Sopenharmony_ci	{ .name = "event", .config = "config:0-7" },
11662306a36Sopenharmony_ci	{ .name = "instance", .config = "config:8-15" },
11762306a36Sopenharmony_ci	{ .name = "umask", .config = "config:16-23"},
11862306a36Sopenharmony_ci	{ .name = "type", .config = "config:56-63"}
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/* Vega20 events */
12262306a36Sopenharmony_cistatic struct amdgpu_pmu_attr vega20_events[NUM_EVENTS_VEGA20_MAX] = {
12362306a36Sopenharmony_ci	{ .name = "xgmi_link0_data_outbound",
12462306a36Sopenharmony_ci			.config = "event=0x7,instance=0x46,umask=0x2" },
12562306a36Sopenharmony_ci	{ .name = "xgmi_link1_data_outbound",
12662306a36Sopenharmony_ci			.config = "event=0x7,instance=0x47,umask=0x2" }
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic struct amdgpu_pmu_type vega20_types[NUM_EVENT_TYPES_VEGA20] = {
13062306a36Sopenharmony_ci	{ .type = AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI,
13162306a36Sopenharmony_ci					.num_of_type = NUM_EVENTS_VEGA20_XGMI }
13262306a36Sopenharmony_ci};
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic struct amdgpu_pmu_config vega20_config = {
13562306a36Sopenharmony_ci	.formats = amdgpu_pmu_formats,
13662306a36Sopenharmony_ci	.num_formats = ARRAY_SIZE(amdgpu_pmu_formats),
13762306a36Sopenharmony_ci	.events = vega20_events,
13862306a36Sopenharmony_ci	.num_events = ARRAY_SIZE(vega20_events),
13962306a36Sopenharmony_ci	.types = vega20_types,
14062306a36Sopenharmony_ci	.num_types = ARRAY_SIZE(vega20_types)
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/* Vega20 data fabric (DF) events */
14462306a36Sopenharmony_cistatic struct amdgpu_pmu_attr df_vega20_formats[NUM_FORMATS_DF_VEGA20] = {
14562306a36Sopenharmony_ci	{ .name = "event", .config = "config:0-7" },
14662306a36Sopenharmony_ci	{ .name = "instance", .config = "config:8-15" },
14762306a36Sopenharmony_ci	{ .name = "umask", .config = "config:16-23"}
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic struct amdgpu_pmu_attr df_vega20_events[NUM_EVENTS_DF_VEGA20] = {
15162306a36Sopenharmony_ci	{ .name = "cake0_pcsout_txdata",
15262306a36Sopenharmony_ci			.config = "event=0x7,instance=0x46,umask=0x2" },
15362306a36Sopenharmony_ci	{ .name = "cake1_pcsout_txdata",
15462306a36Sopenharmony_ci			.config = "event=0x7,instance=0x47,umask=0x2" },
15562306a36Sopenharmony_ci	{ .name = "cake0_pcsout_txmeta",
15662306a36Sopenharmony_ci			.config = "event=0x7,instance=0x46,umask=0x4" },
15762306a36Sopenharmony_ci	{ .name = "cake1_pcsout_txmeta",
15862306a36Sopenharmony_ci			.config = "event=0x7,instance=0x47,umask=0x4" },
15962306a36Sopenharmony_ci	{ .name = "cake0_ftiinstat_reqalloc",
16062306a36Sopenharmony_ci			.config = "event=0xb,instance=0x46,umask=0x4" },
16162306a36Sopenharmony_ci	{ .name = "cake1_ftiinstat_reqalloc",
16262306a36Sopenharmony_ci			.config = "event=0xb,instance=0x47,umask=0x4" },
16362306a36Sopenharmony_ci	{ .name = "cake0_ftiinstat_rspalloc",
16462306a36Sopenharmony_ci			.config = "event=0xb,instance=0x46,umask=0x8" },
16562306a36Sopenharmony_ci	{ .name = "cake1_ftiinstat_rspalloc",
16662306a36Sopenharmony_ci			.config = "event=0xb,instance=0x47,umask=0x8" }
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic struct amdgpu_pmu_config df_vega20_config = {
17062306a36Sopenharmony_ci	.formats = df_vega20_formats,
17162306a36Sopenharmony_ci	.num_formats = ARRAY_SIZE(df_vega20_formats),
17262306a36Sopenharmony_ci	.events = df_vega20_events,
17362306a36Sopenharmony_ci	.num_events = ARRAY_SIZE(df_vega20_events),
17462306a36Sopenharmony_ci	.types = NULL,
17562306a36Sopenharmony_ci	.num_types = 0
17662306a36Sopenharmony_ci};
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/* Arcturus events */
17962306a36Sopenharmony_cistatic struct amdgpu_pmu_attr arcturus_events[NUM_EVENTS_ARCTURUS_MAX] = {
18062306a36Sopenharmony_ci	{ .name = "xgmi_link0_data_outbound",
18162306a36Sopenharmony_ci			.config = "event=0x7,instance=0x4b,umask=0x2" },
18262306a36Sopenharmony_ci	{ .name = "xgmi_link1_data_outbound",
18362306a36Sopenharmony_ci			.config = "event=0x7,instance=0x4c,umask=0x2" },
18462306a36Sopenharmony_ci	{ .name = "xgmi_link2_data_outbound",
18562306a36Sopenharmony_ci			.config = "event=0x7,instance=0x4d,umask=0x2" },
18662306a36Sopenharmony_ci	{ .name = "xgmi_link3_data_outbound",
18762306a36Sopenharmony_ci			.config = "event=0x7,instance=0x4e,umask=0x2" },
18862306a36Sopenharmony_ci	{ .name = "xgmi_link4_data_outbound",
18962306a36Sopenharmony_ci			.config = "event=0x7,instance=0x4f,umask=0x2" },
19062306a36Sopenharmony_ci	{ .name = "xgmi_link5_data_outbound",
19162306a36Sopenharmony_ci			.config = "event=0x7,instance=0x50,umask=0x2" }
19262306a36Sopenharmony_ci};
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic struct amdgpu_pmu_type arcturus_types[NUM_EVENT_TYPES_ARCTURUS] = {
19562306a36Sopenharmony_ci	{ .type = AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI,
19662306a36Sopenharmony_ci				.num_of_type = NUM_EVENTS_ARCTURUS_XGMI }
19762306a36Sopenharmony_ci};
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic struct amdgpu_pmu_config arcturus_config = {
20062306a36Sopenharmony_ci	.formats = amdgpu_pmu_formats,
20162306a36Sopenharmony_ci	.num_formats = ARRAY_SIZE(amdgpu_pmu_formats),
20262306a36Sopenharmony_ci	.events = arcturus_events,
20362306a36Sopenharmony_ci	.num_events = ARRAY_SIZE(arcturus_events),
20462306a36Sopenharmony_ci	.types = arcturus_types,
20562306a36Sopenharmony_ci	.num_types = ARRAY_SIZE(arcturus_types)
20662306a36Sopenharmony_ci};
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/* initialize perf counter */
20962306a36Sopenharmony_cistatic int amdgpu_perf_event_init(struct perf_event *event)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* test the event attr type check for PMU enumeration */
21462306a36Sopenharmony_ci	if (event->attr.type != event->pmu->type)
21562306a36Sopenharmony_ci		return -ENOENT;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* update the hw_perf_event struct with config data */
21862306a36Sopenharmony_ci	hwc->config = event->attr.config;
21962306a36Sopenharmony_ci	hwc->config_base = AMDGPU_PMU_PERF_TYPE_NONE;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return 0;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci/* start perf counter */
22562306a36Sopenharmony_cistatic void amdgpu_perf_start(struct perf_event *event, int flags)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
22862306a36Sopenharmony_ci	struct amdgpu_pmu_entry *pe = container_of(event->pmu,
22962306a36Sopenharmony_ci						  struct amdgpu_pmu_entry,
23062306a36Sopenharmony_ci						  pmu);
23162306a36Sopenharmony_ci	int target_cntr = 0;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
23462306a36Sopenharmony_ci		return;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if ((!pe->adev->df.funcs) ||
23762306a36Sopenharmony_ci	    (!pe->adev->df.funcs->pmc_start))
23862306a36Sopenharmony_ci		return;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
24162306a36Sopenharmony_ci	hwc->state = 0;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	switch (hwc->config_base) {
24462306a36Sopenharmony_ci	case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF:
24562306a36Sopenharmony_ci	case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI:
24662306a36Sopenharmony_ci		if (!(flags & PERF_EF_RELOAD)) {
24762306a36Sopenharmony_ci			target_cntr = pe->adev->df.funcs->pmc_start(pe->adev,
24862306a36Sopenharmony_ci						hwc->config, 0 /* unused */,
24962306a36Sopenharmony_ci						1 /* add counter */);
25062306a36Sopenharmony_ci			if (target_cntr < 0)
25162306a36Sopenharmony_ci				break;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci			hwc->idx = target_cntr;
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		pe->adev->df.funcs->pmc_start(pe->adev, hwc->config,
25762306a36Sopenharmony_ci								hwc->idx, 0);
25862306a36Sopenharmony_ci		break;
25962306a36Sopenharmony_ci	default:
26062306a36Sopenharmony_ci		break;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	perf_event_update_userpage(event);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/* read perf counter */
26762306a36Sopenharmony_cistatic void amdgpu_perf_read(struct perf_event *event)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
27062306a36Sopenharmony_ci	struct amdgpu_pmu_entry *pe = container_of(event->pmu,
27162306a36Sopenharmony_ci						  struct amdgpu_pmu_entry,
27262306a36Sopenharmony_ci						  pmu);
27362306a36Sopenharmony_ci	u64 count, prev;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if ((!pe->adev->df.funcs) ||
27662306a36Sopenharmony_ci	    (!pe->adev->df.funcs->pmc_get_count))
27762306a36Sopenharmony_ci		return;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	prev = local64_read(&hwc->prev_count);
28062306a36Sopenharmony_ci	do {
28162306a36Sopenharmony_ci		switch (hwc->config_base) {
28262306a36Sopenharmony_ci		case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF:
28362306a36Sopenharmony_ci		case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI:
28462306a36Sopenharmony_ci			pe->adev->df.funcs->pmc_get_count(pe->adev,
28562306a36Sopenharmony_ci						hwc->config, hwc->idx, &count);
28662306a36Sopenharmony_ci			break;
28762306a36Sopenharmony_ci		default:
28862306a36Sopenharmony_ci			count = 0;
28962306a36Sopenharmony_ci			break;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci	} while (!local64_try_cmpxchg(&hwc->prev_count, &prev, count));
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	local64_add(count - prev, &event->count);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci/* stop perf counter */
29762306a36Sopenharmony_cistatic void amdgpu_perf_stop(struct perf_event *event, int flags)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
30062306a36Sopenharmony_ci	struct amdgpu_pmu_entry *pe = container_of(event->pmu,
30162306a36Sopenharmony_ci						  struct amdgpu_pmu_entry,
30262306a36Sopenharmony_ci						  pmu);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (hwc->state & PERF_HES_UPTODATE)
30562306a36Sopenharmony_ci		return;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if ((!pe->adev->df.funcs) ||
30862306a36Sopenharmony_ci	    (!pe->adev->df.funcs->pmc_stop))
30962306a36Sopenharmony_ci		return;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	switch (hwc->config_base) {
31262306a36Sopenharmony_ci	case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF:
31362306a36Sopenharmony_ci	case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI:
31462306a36Sopenharmony_ci		pe->adev->df.funcs->pmc_stop(pe->adev, hwc->config, hwc->idx,
31562306a36Sopenharmony_ci									0);
31662306a36Sopenharmony_ci		break;
31762306a36Sopenharmony_ci	default:
31862306a36Sopenharmony_ci		break;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
32262306a36Sopenharmony_ci	hwc->state |= PERF_HES_STOPPED;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (hwc->state & PERF_HES_UPTODATE)
32562306a36Sopenharmony_ci		return;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	amdgpu_perf_read(event);
32862306a36Sopenharmony_ci	hwc->state |= PERF_HES_UPTODATE;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci/* add perf counter */
33262306a36Sopenharmony_cistatic int amdgpu_perf_add(struct perf_event *event, int flags)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
33562306a36Sopenharmony_ci	int retval = 0, target_cntr;
33662306a36Sopenharmony_ci	struct amdgpu_pmu_entry *pe = container_of(event->pmu,
33762306a36Sopenharmony_ci						  struct amdgpu_pmu_entry,
33862306a36Sopenharmony_ci						  pmu);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if ((!pe->adev->df.funcs) ||
34162306a36Sopenharmony_ci	    (!pe->adev->df.funcs->pmc_start))
34262306a36Sopenharmony_ci		return -EINVAL;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	switch (pe->pmu_perf_type) {
34562306a36Sopenharmony_ci	case AMDGPU_PMU_PERF_TYPE_DF:
34662306a36Sopenharmony_ci		hwc->config_base = AMDGPU_PMU_EVENT_CONFIG_TYPE_DF;
34762306a36Sopenharmony_ci		break;
34862306a36Sopenharmony_ci	case AMDGPU_PMU_PERF_TYPE_ALL:
34962306a36Sopenharmony_ci		hwc->config_base = (hwc->config >>
35062306a36Sopenharmony_ci					AMDGPU_PMU_EVENT_CONFIG_TYPE_SHIFT) &
35162306a36Sopenharmony_ci					AMDGPU_PMU_EVENT_CONFIG_TYPE_MASK;
35262306a36Sopenharmony_ci		break;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	switch (hwc->config_base) {
35862306a36Sopenharmony_ci	case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF:
35962306a36Sopenharmony_ci	case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI:
36062306a36Sopenharmony_ci		target_cntr = pe->adev->df.funcs->pmc_start(pe->adev,
36162306a36Sopenharmony_ci						hwc->config, 0 /* unused */,
36262306a36Sopenharmony_ci						1 /* add counter */);
36362306a36Sopenharmony_ci		if (target_cntr < 0)
36462306a36Sopenharmony_ci			retval = target_cntr;
36562306a36Sopenharmony_ci		else
36662306a36Sopenharmony_ci			hwc->idx = target_cntr;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci		break;
36962306a36Sopenharmony_ci	default:
37062306a36Sopenharmony_ci		return 0;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (retval)
37462306a36Sopenharmony_ci		return retval;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (flags & PERF_EF_START)
37762306a36Sopenharmony_ci		amdgpu_perf_start(event, PERF_EF_RELOAD);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return retval;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/* delete perf counter  */
38362306a36Sopenharmony_cistatic void amdgpu_perf_del(struct perf_event *event, int flags)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
38662306a36Sopenharmony_ci	struct amdgpu_pmu_entry *pe = container_of(event->pmu,
38762306a36Sopenharmony_ci						  struct amdgpu_pmu_entry,
38862306a36Sopenharmony_ci						  pmu);
38962306a36Sopenharmony_ci	if ((!pe->adev->df.funcs) ||
39062306a36Sopenharmony_ci	    (!pe->adev->df.funcs->pmc_stop))
39162306a36Sopenharmony_ci		return;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	amdgpu_perf_stop(event, PERF_EF_UPDATE);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	switch (hwc->config_base) {
39662306a36Sopenharmony_ci	case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF:
39762306a36Sopenharmony_ci	case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI:
39862306a36Sopenharmony_ci		pe->adev->df.funcs->pmc_stop(pe->adev, hwc->config, hwc->idx,
39962306a36Sopenharmony_ci									1);
40062306a36Sopenharmony_ci		break;
40162306a36Sopenharmony_ci	default:
40262306a36Sopenharmony_ci		break;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	perf_event_update_userpage(event);
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic void amdgpu_pmu_create_event_attrs_by_type(
40962306a36Sopenharmony_ci				struct attribute_group *attr_group,
41062306a36Sopenharmony_ci				struct amdgpu_pmu_event_attribute *pmu_attr,
41162306a36Sopenharmony_ci				struct amdgpu_pmu_attr events[],
41262306a36Sopenharmony_ci				int s_offset,
41362306a36Sopenharmony_ci				int e_offset,
41462306a36Sopenharmony_ci				unsigned int type)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	int i;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	pmu_attr += s_offset;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	for (i = s_offset; i < e_offset; i++) {
42162306a36Sopenharmony_ci		attr_group->attrs[i] = &pmu_attr->attr.attr;
42262306a36Sopenharmony_ci		sysfs_attr_init(&pmu_attr->attr.attr);
42362306a36Sopenharmony_ci		pmu_attr->attr.attr.name = events[i].name;
42462306a36Sopenharmony_ci		pmu_attr->attr.attr.mode = 0444;
42562306a36Sopenharmony_ci		pmu_attr->attr.show = amdgpu_pmu_event_show;
42662306a36Sopenharmony_ci		pmu_attr->event_str = events[i].config;
42762306a36Sopenharmony_ci		pmu_attr->type = type;
42862306a36Sopenharmony_ci		pmu_attr++;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic void amdgpu_pmu_create_attrs(struct attribute_group *attr_group,
43362306a36Sopenharmony_ci				struct amdgpu_pmu_event_attribute *pmu_attr,
43462306a36Sopenharmony_ci				struct amdgpu_pmu_attr events[],
43562306a36Sopenharmony_ci				int num_events)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	amdgpu_pmu_create_event_attrs_by_type(attr_group, pmu_attr, events, 0,
43862306a36Sopenharmony_ci				num_events, AMDGPU_PMU_EVENT_CONFIG_TYPE_NONE);
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int amdgpu_pmu_alloc_pmu_attrs(
44362306a36Sopenharmony_ci				struct attribute_group *fmt_attr_group,
44462306a36Sopenharmony_ci				struct amdgpu_pmu_event_attribute **fmt_attr,
44562306a36Sopenharmony_ci				struct attribute_group *evt_attr_group,
44662306a36Sopenharmony_ci				struct amdgpu_pmu_event_attribute **evt_attr,
44762306a36Sopenharmony_ci				struct amdgpu_pmu_config *config)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	*fmt_attr = kcalloc(config->num_formats, sizeof(**fmt_attr),
45062306a36Sopenharmony_ci								GFP_KERNEL);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (!(*fmt_attr))
45362306a36Sopenharmony_ci		return -ENOMEM;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	fmt_attr_group->attrs = kcalloc(config->num_formats + 1,
45662306a36Sopenharmony_ci				sizeof(*fmt_attr_group->attrs), GFP_KERNEL);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (!fmt_attr_group->attrs)
45962306a36Sopenharmony_ci		goto err_fmt_attr_grp;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	*evt_attr = kcalloc(config->num_events, sizeof(**evt_attr), GFP_KERNEL);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (!(*evt_attr))
46462306a36Sopenharmony_ci		goto err_evt_attr;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	evt_attr_group->attrs = kcalloc(config->num_events + 1,
46762306a36Sopenharmony_ci				sizeof(*evt_attr_group->attrs), GFP_KERNEL);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (!evt_attr_group->attrs)
47062306a36Sopenharmony_ci		goto err_evt_attr_grp;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	return 0;
47362306a36Sopenharmony_cierr_evt_attr_grp:
47462306a36Sopenharmony_ci	kfree(*evt_attr);
47562306a36Sopenharmony_cierr_evt_attr:
47662306a36Sopenharmony_ci	kfree(fmt_attr_group->attrs);
47762306a36Sopenharmony_cierr_fmt_attr_grp:
47862306a36Sopenharmony_ci	kfree(*fmt_attr);
47962306a36Sopenharmony_ci	return -ENOMEM;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci/* init pmu tracking per pmu type */
48362306a36Sopenharmony_cistatic int init_pmu_entry_by_type_and_add(struct amdgpu_pmu_entry *pmu_entry,
48462306a36Sopenharmony_ci			struct amdgpu_pmu_config *config)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	const struct attribute_group *attr_groups[] = {
48762306a36Sopenharmony_ci		&pmu_entry->fmt_attr_group,
48862306a36Sopenharmony_ci		&pmu_entry->evt_attr_group,
48962306a36Sopenharmony_ci		NULL
49062306a36Sopenharmony_ci	};
49162306a36Sopenharmony_ci	char pmu_name[PMU_NAME_SIZE];
49262306a36Sopenharmony_ci	int ret = 0, total_num_events = 0;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	pmu_entry->pmu = (struct pmu){
49562306a36Sopenharmony_ci		.event_init = amdgpu_perf_event_init,
49662306a36Sopenharmony_ci		.add = amdgpu_perf_add,
49762306a36Sopenharmony_ci		.del = amdgpu_perf_del,
49862306a36Sopenharmony_ci		.start = amdgpu_perf_start,
49962306a36Sopenharmony_ci		.stop = amdgpu_perf_stop,
50062306a36Sopenharmony_ci		.read = amdgpu_perf_read,
50162306a36Sopenharmony_ci		.task_ctx_nr = perf_invalid_context,
50262306a36Sopenharmony_ci	};
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	ret = amdgpu_pmu_alloc_pmu_attrs(&pmu_entry->fmt_attr_group,
50562306a36Sopenharmony_ci					&pmu_entry->fmt_attr,
50662306a36Sopenharmony_ci					&pmu_entry->evt_attr_group,
50762306a36Sopenharmony_ci					&pmu_entry->evt_attr,
50862306a36Sopenharmony_ci					config);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (ret)
51162306a36Sopenharmony_ci		goto err_out;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	amdgpu_pmu_create_attrs(&pmu_entry->fmt_attr_group, pmu_entry->fmt_attr,
51462306a36Sopenharmony_ci					config->formats, config->num_formats);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (pmu_entry->pmu_perf_type == AMDGPU_PMU_PERF_TYPE_ALL) {
51762306a36Sopenharmony_ci		int i;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		for (i = 0; i < config->num_types; i++) {
52062306a36Sopenharmony_ci			amdgpu_pmu_create_event_attrs_by_type(
52162306a36Sopenharmony_ci					&pmu_entry->evt_attr_group,
52262306a36Sopenharmony_ci					pmu_entry->evt_attr,
52362306a36Sopenharmony_ci					config->events,
52462306a36Sopenharmony_ci					total_num_events,
52562306a36Sopenharmony_ci					total_num_events +
52662306a36Sopenharmony_ci						config->types[i].num_of_type,
52762306a36Sopenharmony_ci					config->types[i].type);
52862306a36Sopenharmony_ci			total_num_events += config->types[i].num_of_type;
52962306a36Sopenharmony_ci		}
53062306a36Sopenharmony_ci	} else {
53162306a36Sopenharmony_ci		amdgpu_pmu_create_attrs(&pmu_entry->evt_attr_group,
53262306a36Sopenharmony_ci					pmu_entry->evt_attr,
53362306a36Sopenharmony_ci					config->events, config->num_events);
53462306a36Sopenharmony_ci		total_num_events = config->num_events;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	pmu_entry->pmu.attr_groups = kmemdup(attr_groups, sizeof(attr_groups),
53862306a36Sopenharmony_ci								GFP_KERNEL);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (!pmu_entry->pmu.attr_groups) {
54162306a36Sopenharmony_ci		ret = -ENOMEM;
54262306a36Sopenharmony_ci		goto err_attr_group;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	snprintf(pmu_name, PMU_NAME_SIZE, "%s_%d", pmu_entry->pmu_file_prefix,
54662306a36Sopenharmony_ci				adev_to_drm(pmu_entry->adev)->primary->index);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	ret = perf_pmu_register(&pmu_entry->pmu, pmu_name, -1);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (ret)
55162306a36Sopenharmony_ci		goto err_register;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (pmu_entry->pmu_perf_type != AMDGPU_PMU_PERF_TYPE_ALL)
55462306a36Sopenharmony_ci		pr_info("Detected AMDGPU %s Counters. # of Counters = %d.\n",
55562306a36Sopenharmony_ci				pmu_entry->pmu_type_name, total_num_events);
55662306a36Sopenharmony_ci	else
55762306a36Sopenharmony_ci		pr_info("Detected AMDGPU %d Perf Events.\n", total_num_events);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	list_add_tail(&pmu_entry->entry, &amdgpu_pmu_list);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	return 0;
56362306a36Sopenharmony_cierr_register:
56462306a36Sopenharmony_ci	kfree(pmu_entry->pmu.attr_groups);
56562306a36Sopenharmony_cierr_attr_group:
56662306a36Sopenharmony_ci	kfree(pmu_entry->fmt_attr_group.attrs);
56762306a36Sopenharmony_ci	kfree(pmu_entry->fmt_attr);
56862306a36Sopenharmony_ci	kfree(pmu_entry->evt_attr_group.attrs);
56962306a36Sopenharmony_ci	kfree(pmu_entry->evt_attr);
57062306a36Sopenharmony_cierr_out:
57162306a36Sopenharmony_ci	pr_warn("Error initializing AMDGPU %s PMUs.\n",
57262306a36Sopenharmony_ci						pmu_entry->pmu_type_name);
57362306a36Sopenharmony_ci	return ret;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci/* destroy all pmu data associated with target device */
57762306a36Sopenharmony_civoid amdgpu_pmu_fini(struct amdgpu_device *adev)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct amdgpu_pmu_entry *pe, *temp;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	list_for_each_entry_safe(pe, temp, &amdgpu_pmu_list, entry) {
58262306a36Sopenharmony_ci		if (pe->adev != adev)
58362306a36Sopenharmony_ci			continue;
58462306a36Sopenharmony_ci		list_del(&pe->entry);
58562306a36Sopenharmony_ci		perf_pmu_unregister(&pe->pmu);
58662306a36Sopenharmony_ci		kfree(pe->pmu.attr_groups);
58762306a36Sopenharmony_ci		kfree(pe->fmt_attr_group.attrs);
58862306a36Sopenharmony_ci		kfree(pe->fmt_attr);
58962306a36Sopenharmony_ci		kfree(pe->evt_attr_group.attrs);
59062306a36Sopenharmony_ci		kfree(pe->evt_attr);
59162306a36Sopenharmony_ci		kfree(pe);
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic struct amdgpu_pmu_entry *create_pmu_entry(struct amdgpu_device *adev,
59662306a36Sopenharmony_ci						unsigned int pmu_type,
59762306a36Sopenharmony_ci						char *pmu_type_name,
59862306a36Sopenharmony_ci						char *pmu_file_prefix)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct amdgpu_pmu_entry *pmu_entry;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	pmu_entry = kzalloc(sizeof(struct amdgpu_pmu_entry), GFP_KERNEL);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (!pmu_entry)
60562306a36Sopenharmony_ci		return pmu_entry;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	pmu_entry->adev = adev;
60862306a36Sopenharmony_ci	pmu_entry->fmt_attr_group.name = "format";
60962306a36Sopenharmony_ci	pmu_entry->fmt_attr_group.attrs = NULL;
61062306a36Sopenharmony_ci	pmu_entry->evt_attr_group.name = "events";
61162306a36Sopenharmony_ci	pmu_entry->evt_attr_group.attrs = NULL;
61262306a36Sopenharmony_ci	pmu_entry->pmu_perf_type = pmu_type;
61362306a36Sopenharmony_ci	pmu_entry->pmu_type_name = pmu_type_name;
61462306a36Sopenharmony_ci	pmu_entry->pmu_file_prefix = pmu_file_prefix;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	return pmu_entry;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci/* init amdgpu_pmu */
62062306a36Sopenharmony_ciint amdgpu_pmu_init(struct amdgpu_device *adev)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	int ret = 0;
62362306a36Sopenharmony_ci	struct amdgpu_pmu_entry *pmu_entry, *pmu_entry_df;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	switch (adev->asic_type) {
62662306a36Sopenharmony_ci	case CHIP_VEGA20:
62762306a36Sopenharmony_ci		pmu_entry_df = create_pmu_entry(adev, AMDGPU_PMU_PERF_TYPE_DF,
62862306a36Sopenharmony_ci						"DF", "amdgpu_df");
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci		if (!pmu_entry_df)
63162306a36Sopenharmony_ci			return -ENOMEM;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci		ret = init_pmu_entry_by_type_and_add(pmu_entry_df,
63462306a36Sopenharmony_ci							&df_vega20_config);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci		if (ret) {
63762306a36Sopenharmony_ci			kfree(pmu_entry_df);
63862306a36Sopenharmony_ci			return ret;
63962306a36Sopenharmony_ci		}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci		pmu_entry = create_pmu_entry(adev, AMDGPU_PMU_PERF_TYPE_ALL,
64262306a36Sopenharmony_ci						"", "amdgpu");
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		if (!pmu_entry) {
64562306a36Sopenharmony_ci			amdgpu_pmu_fini(adev);
64662306a36Sopenharmony_ci			return -ENOMEM;
64762306a36Sopenharmony_ci		}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci		ret = init_pmu_entry_by_type_and_add(pmu_entry,
65062306a36Sopenharmony_ci							&vega20_config);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		if (ret) {
65362306a36Sopenharmony_ci			kfree(pmu_entry);
65462306a36Sopenharmony_ci			amdgpu_pmu_fini(adev);
65562306a36Sopenharmony_ci			return ret;
65662306a36Sopenharmony_ci		}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		break;
65962306a36Sopenharmony_ci	case CHIP_ARCTURUS:
66062306a36Sopenharmony_ci		pmu_entry = create_pmu_entry(adev, AMDGPU_PMU_PERF_TYPE_ALL,
66162306a36Sopenharmony_ci						"", "amdgpu");
66262306a36Sopenharmony_ci		if (!pmu_entry)
66362306a36Sopenharmony_ci			return -ENOMEM;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		ret = init_pmu_entry_by_type_and_add(pmu_entry,
66662306a36Sopenharmony_ci							&arcturus_config);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci		if (ret) {
66962306a36Sopenharmony_ci			kfree(pmu_entry);
67062306a36Sopenharmony_ci			return -ENOMEM;
67162306a36Sopenharmony_ci		}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		break;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	default:
67662306a36Sopenharmony_ci		return 0;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return ret;
68062306a36Sopenharmony_ci}
681