18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright(C) 2015 Linaro Limited. All rights reserved.
48c2ecf20Sopenharmony_ci * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <stdbool.h>
88c2ecf20Sopenharmony_ci#include <linux/coresight-pmu.h>
98c2ecf20Sopenharmony_ci#include <linux/zalloc.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "../../util/auxtrace.h"
128c2ecf20Sopenharmony_ci#include "../../util/debug.h"
138c2ecf20Sopenharmony_ci#include "../../util/evlist.h"
148c2ecf20Sopenharmony_ci#include "../../util/pmu.h"
158c2ecf20Sopenharmony_ci#include "cs-etm.h"
168c2ecf20Sopenharmony_ci#include "arm-spe.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	struct perf_pmu **arm_spe_pmus = NULL;
218c2ecf20Sopenharmony_ci	int ret, i, nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
228c2ecf20Sopenharmony_ci	/* arm_spe_xxxxxxxxx\0 */
238c2ecf20Sopenharmony_ci	char arm_spe_pmu_name[sizeof(ARM_SPE_PMU_NAME) + 10];
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	arm_spe_pmus = zalloc(sizeof(struct perf_pmu *) * nr_cpus);
268c2ecf20Sopenharmony_ci	if (!arm_spe_pmus) {
278c2ecf20Sopenharmony_ci		pr_err("spes alloc failed\n");
288c2ecf20Sopenharmony_ci		*err = -ENOMEM;
298c2ecf20Sopenharmony_ci		return NULL;
308c2ecf20Sopenharmony_ci	}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	for (i = 0; i < nr_cpus; i++) {
338c2ecf20Sopenharmony_ci		ret = sprintf(arm_spe_pmu_name, "%s%d", ARM_SPE_PMU_NAME, i);
348c2ecf20Sopenharmony_ci		if (ret < 0) {
358c2ecf20Sopenharmony_ci			pr_err("sprintf failed\n");
368c2ecf20Sopenharmony_ci			*err = -ENOMEM;
378c2ecf20Sopenharmony_ci			return NULL;
388c2ecf20Sopenharmony_ci		}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci		arm_spe_pmus[*nr_spes] = perf_pmu__find(arm_spe_pmu_name);
418c2ecf20Sopenharmony_ci		if (arm_spe_pmus[*nr_spes]) {
428c2ecf20Sopenharmony_ci			pr_debug2("%s %d: arm_spe_pmu %d type %d name %s\n",
438c2ecf20Sopenharmony_ci				 __func__, __LINE__, *nr_spes,
448c2ecf20Sopenharmony_ci				 arm_spe_pmus[*nr_spes]->type,
458c2ecf20Sopenharmony_ci				 arm_spe_pmus[*nr_spes]->name);
468c2ecf20Sopenharmony_ci			(*nr_spes)++;
478c2ecf20Sopenharmony_ci		}
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	return arm_spe_pmus;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistruct auxtrace_record
548c2ecf20Sopenharmony_ci*auxtrace_record__init(struct evlist *evlist, int *err)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct perf_pmu	*cs_etm_pmu;
578c2ecf20Sopenharmony_ci	struct evsel *evsel;
588c2ecf20Sopenharmony_ci	bool found_etm = false;
598c2ecf20Sopenharmony_ci	struct perf_pmu *found_spe = NULL;
608c2ecf20Sopenharmony_ci	struct perf_pmu **arm_spe_pmus = NULL;
618c2ecf20Sopenharmony_ci	int nr_spes = 0;
628c2ecf20Sopenharmony_ci	int i = 0;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (!evlist)
658c2ecf20Sopenharmony_ci		return NULL;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
688c2ecf20Sopenharmony_ci	arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	evlist__for_each_entry(evlist, evsel) {
718c2ecf20Sopenharmony_ci		if (cs_etm_pmu &&
728c2ecf20Sopenharmony_ci		    evsel->core.attr.type == cs_etm_pmu->type)
738c2ecf20Sopenharmony_ci			found_etm = true;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci		if (!nr_spes || found_spe)
768c2ecf20Sopenharmony_ci			continue;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci		for (i = 0; i < nr_spes; i++) {
798c2ecf20Sopenharmony_ci			if (evsel->core.attr.type == arm_spe_pmus[i]->type) {
808c2ecf20Sopenharmony_ci				found_spe = arm_spe_pmus[i];
818c2ecf20Sopenharmony_ci				break;
828c2ecf20Sopenharmony_ci			}
838c2ecf20Sopenharmony_ci		}
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci	free(arm_spe_pmus);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (found_etm && found_spe) {
888c2ecf20Sopenharmony_ci		pr_err("Concurrent ARM Coresight ETM and SPE operation not currently supported\n");
898c2ecf20Sopenharmony_ci		*err = -EOPNOTSUPP;
908c2ecf20Sopenharmony_ci		return NULL;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (found_etm)
948c2ecf20Sopenharmony_ci		return cs_etm_record_init(err);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci#if defined(__aarch64__)
978c2ecf20Sopenharmony_ci	if (found_spe)
988c2ecf20Sopenharmony_ci		return arm_spe_recording_init(err, found_spe);
998c2ecf20Sopenharmony_ci#endif
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/*
1028c2ecf20Sopenharmony_ci	 * Clear 'err' even if we haven't found an event - that way perf
1038c2ecf20Sopenharmony_ci	 * record can still be used even if tracers aren't present.  The NULL
1048c2ecf20Sopenharmony_ci	 * return value will take care of telling the infrastructure HW tracing
1058c2ecf20Sopenharmony_ci	 * isn't available.
1068c2ecf20Sopenharmony_ci	 */
1078c2ecf20Sopenharmony_ci	*err = 0;
1088c2ecf20Sopenharmony_ci	return NULL;
1098c2ecf20Sopenharmony_ci}
110