18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PPC 64 oprofile support:
48c2ecf20Sopenharmony_ci * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
58c2ecf20Sopenharmony_ci * PPC 32 oprofile support: (based on PPC 64 support)
68c2ecf20Sopenharmony_ci * Copyright (C) Freescale Semiconductor, Inc 2004
78c2ecf20Sopenharmony_ci *	Author: Andy Fleming
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Based on alpha version.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/oprofile.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/smp.h>
158c2ecf20Sopenharmony_ci#include <linux/errno.h>
168c2ecf20Sopenharmony_ci#include <asm/ptrace.h>
178c2ecf20Sopenharmony_ci#include <asm/pmc.h>
188c2ecf20Sopenharmony_ci#include <asm/cputable.h>
198c2ecf20Sopenharmony_ci#include <asm/oprofile_impl.h>
208c2ecf20Sopenharmony_ci#include <asm/firmware.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic struct op_powerpc_model *model;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic struct op_counter_config ctr[OP_MAX_COUNTER];
258c2ecf20Sopenharmony_cistatic struct op_system_config sys;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int op_per_cpu_rc;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic void op_handle_interrupt(struct pt_regs *regs)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	model->handle_interrupt(regs, ctr);
328c2ecf20Sopenharmony_ci}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic void op_powerpc_cpu_setup(void *dummy)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	int ret;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	ret = model->cpu_setup(ctr);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	if (ret != 0)
418c2ecf20Sopenharmony_ci		op_per_cpu_rc = ret;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int op_powerpc_setup(void)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	int err;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	op_per_cpu_rc = 0;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/* Grab the hardware */
518c2ecf20Sopenharmony_ci	err = reserve_pmc_hardware(op_handle_interrupt);
528c2ecf20Sopenharmony_ci	if (err)
538c2ecf20Sopenharmony_ci		return err;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	/* Pre-compute the values to stuff in the hardware registers.  */
568c2ecf20Sopenharmony_ci	op_per_cpu_rc = model->reg_setup(ctr, &sys, model->num_counters);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (op_per_cpu_rc)
598c2ecf20Sopenharmony_ci		goto out;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	/* Configure the registers on all cpus.	 If an error occurs on one
628c2ecf20Sopenharmony_ci	 * of the cpus, op_per_cpu_rc will be set to the error */
638c2ecf20Sopenharmony_ci	on_each_cpu(op_powerpc_cpu_setup, NULL, 1);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ciout:	if (op_per_cpu_rc) {
668c2ecf20Sopenharmony_ci		/* error on setup release the performance counter hardware */
678c2ecf20Sopenharmony_ci		release_pmc_hardware();
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return op_per_cpu_rc;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic void op_powerpc_shutdown(void)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	release_pmc_hardware();
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic void op_powerpc_cpu_start(void *dummy)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	/* If any of the cpus have return an error, set the
818c2ecf20Sopenharmony_ci	 * global flag to the error so it can be returned
828c2ecf20Sopenharmony_ci	 * to the generic OProfile caller.
838c2ecf20Sopenharmony_ci	 */
848c2ecf20Sopenharmony_ci	int ret;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	ret = model->start(ctr);
878c2ecf20Sopenharmony_ci	if (ret != 0)
888c2ecf20Sopenharmony_ci		op_per_cpu_rc = ret;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int op_powerpc_start(void)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	op_per_cpu_rc = 0;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (model->global_start)
968c2ecf20Sopenharmony_ci		return model->global_start(ctr);
978c2ecf20Sopenharmony_ci	if (model->start) {
988c2ecf20Sopenharmony_ci		on_each_cpu(op_powerpc_cpu_start, NULL, 1);
998c2ecf20Sopenharmony_ci		return op_per_cpu_rc;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci	return -EIO; /* No start function is defined for this
1028c2ecf20Sopenharmony_ci			power architecture */
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic inline void op_powerpc_cpu_stop(void *dummy)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	model->stop();
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic void op_powerpc_stop(void)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	if (model->stop)
1138c2ecf20Sopenharmony_ci		on_each_cpu(op_powerpc_cpu_stop, NULL, 1);
1148c2ecf20Sopenharmony_ci        if (model->global_stop)
1158c2ecf20Sopenharmony_ci                model->global_stop();
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int op_powerpc_create_files(struct dentry *root)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	int i;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
1238c2ecf20Sopenharmony_ci	/*
1248c2ecf20Sopenharmony_ci	 * There is one mmcr0, mmcr1 and mmcra for setting the events for
1258c2ecf20Sopenharmony_ci	 * all of the counters.
1268c2ecf20Sopenharmony_ci	 */
1278c2ecf20Sopenharmony_ci	oprofilefs_create_ulong(root, "mmcr0", &sys.mmcr0);
1288c2ecf20Sopenharmony_ci	oprofilefs_create_ulong(root, "mmcr1", &sys.mmcr1);
1298c2ecf20Sopenharmony_ci	oprofilefs_create_ulong(root, "mmcra", &sys.mmcra);
1308c2ecf20Sopenharmony_ci#ifdef CONFIG_OPROFILE_CELL
1318c2ecf20Sopenharmony_ci	/* create a file the user tool can check to see what level of profiling
1328c2ecf20Sopenharmony_ci	 * support exits with this kernel. Initialize bit mask to indicate
1338c2ecf20Sopenharmony_ci	 * what support the kernel has:
1348c2ecf20Sopenharmony_ci	 * bit 0      -  Supports SPU event profiling in addition to PPU
1358c2ecf20Sopenharmony_ci	 *               event and cycles; and SPU cycle profiling
1368c2ecf20Sopenharmony_ci	 * bits 1-31  -  Currently unused.
1378c2ecf20Sopenharmony_ci	 *
1388c2ecf20Sopenharmony_ci	 * If the file does not exist, then the kernel only supports SPU
1398c2ecf20Sopenharmony_ci	 * cycle profiling, PPU event and cycle profiling.
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci	oprofilefs_create_ulong(root, "cell_support", &sys.cell_support);
1428c2ecf20Sopenharmony_ci	sys.cell_support = 0x1; /* Note, the user OProfile tool must check
1438c2ecf20Sopenharmony_ci				 * that this bit is set before attempting to
1448c2ecf20Sopenharmony_ci				 * user SPU event profiling.  Older kernels
1458c2ecf20Sopenharmony_ci				 * will not have this file, hence the user
1468c2ecf20Sopenharmony_ci				 * tool is not allowed to do SPU event
1478c2ecf20Sopenharmony_ci				 * profiling on older kernels.  Older kernels
1488c2ecf20Sopenharmony_ci				 * will accept SPU events but collected data
1498c2ecf20Sopenharmony_ci				 * is garbage.
1508c2ecf20Sopenharmony_ci				 */
1518c2ecf20Sopenharmony_ci#endif
1528c2ecf20Sopenharmony_ci#endif
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	for (i = 0; i < model->num_counters; ++i) {
1558c2ecf20Sopenharmony_ci		struct dentry *dir;
1568c2ecf20Sopenharmony_ci		char buf[4];
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		snprintf(buf, sizeof buf, "%d", i);
1598c2ecf20Sopenharmony_ci		dir = oprofilefs_mkdir(root, buf);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		oprofilefs_create_ulong(dir, "enabled", &ctr[i].enabled);
1628c2ecf20Sopenharmony_ci		oprofilefs_create_ulong(dir, "event", &ctr[i].event);
1638c2ecf20Sopenharmony_ci		oprofilefs_create_ulong(dir, "count", &ctr[i].count);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		/*
1668c2ecf20Sopenharmony_ci		 * Classic PowerPC doesn't support per-counter
1678c2ecf20Sopenharmony_ci		 * control like this, but the options are
1688c2ecf20Sopenharmony_ci		 * expected, so they remain.  For Freescale
1698c2ecf20Sopenharmony_ci		 * Book-E style performance monitors, we do
1708c2ecf20Sopenharmony_ci		 * support them.
1718c2ecf20Sopenharmony_ci		 */
1728c2ecf20Sopenharmony_ci		oprofilefs_create_ulong(dir, "kernel", &ctr[i].kernel);
1738c2ecf20Sopenharmony_ci		oprofilefs_create_ulong(dir, "user", &ctr[i].user);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		oprofilefs_create_ulong(dir, "unit_mask", &ctr[i].unit_mask);
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	oprofilefs_create_ulong(root, "enable_kernel", &sys.enable_kernel);
1798c2ecf20Sopenharmony_ci	oprofilefs_create_ulong(root, "enable_user", &sys.enable_user);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	/* Default to tracing both kernel and user */
1828c2ecf20Sopenharmony_ci	sys.enable_kernel = 1;
1838c2ecf20Sopenharmony_ci	sys.enable_user = 1;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ciint __init oprofile_arch_init(struct oprofile_operations *ops)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	if (!cur_cpu_spec->oprofile_cpu_type)
1918c2ecf20Sopenharmony_ci		return -ENODEV;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	switch (cur_cpu_spec->oprofile_type) {
1948c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_64
1958c2ecf20Sopenharmony_ci#ifdef CONFIG_OPROFILE_CELL
1968c2ecf20Sopenharmony_ci		case PPC_OPROFILE_CELL:
1978c2ecf20Sopenharmony_ci			if (firmware_has_feature(FW_FEATURE_LPAR))
1988c2ecf20Sopenharmony_ci				return -ENODEV;
1998c2ecf20Sopenharmony_ci			model = &op_model_cell;
2008c2ecf20Sopenharmony_ci			ops->sync_start = model->sync_start;
2018c2ecf20Sopenharmony_ci			ops->sync_stop = model->sync_stop;
2028c2ecf20Sopenharmony_ci			break;
2038c2ecf20Sopenharmony_ci#endif
2048c2ecf20Sopenharmony_ci		case PPC_OPROFILE_POWER4:
2058c2ecf20Sopenharmony_ci			model = &op_model_power4;
2068c2ecf20Sopenharmony_ci			break;
2078c2ecf20Sopenharmony_ci		case PPC_OPROFILE_PA6T:
2088c2ecf20Sopenharmony_ci			model = &op_model_pa6t;
2098c2ecf20Sopenharmony_ci			break;
2108c2ecf20Sopenharmony_ci#endif
2118c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_32
2128c2ecf20Sopenharmony_ci		case PPC_OPROFILE_G4:
2138c2ecf20Sopenharmony_ci			model = &op_model_7450;
2148c2ecf20Sopenharmony_ci			break;
2158c2ecf20Sopenharmony_ci#endif
2168c2ecf20Sopenharmony_ci#if defined(CONFIG_FSL_EMB_PERFMON)
2178c2ecf20Sopenharmony_ci		case PPC_OPROFILE_FSL_EMB:
2188c2ecf20Sopenharmony_ci			model = &op_model_fsl_emb;
2198c2ecf20Sopenharmony_ci			break;
2208c2ecf20Sopenharmony_ci#endif
2218c2ecf20Sopenharmony_ci		default:
2228c2ecf20Sopenharmony_ci			return -ENODEV;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	model->num_counters = cur_cpu_spec->num_pmcs;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	ops->cpu_type = cur_cpu_spec->oprofile_cpu_type;
2288c2ecf20Sopenharmony_ci	ops->create_files = op_powerpc_create_files;
2298c2ecf20Sopenharmony_ci	ops->setup = op_powerpc_setup;
2308c2ecf20Sopenharmony_ci	ops->shutdown = op_powerpc_shutdown;
2318c2ecf20Sopenharmony_ci	ops->start = op_powerpc_start;
2328c2ecf20Sopenharmony_ci	ops->stop = op_powerpc_stop;
2338c2ecf20Sopenharmony_ci	ops->backtrace = op_powerpc_backtrace;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "oprofile: using %s performance monitoring.\n",
2368c2ecf20Sopenharmony_ci	       ops->cpu_type);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return 0;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_civoid oprofile_arch_exit(void)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci}
244