18c2ecf20Sopenharmony_ci/**
28c2ecf20Sopenharmony_ci * @file timer_int.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * @remark Copyright 2002 OProfile authors
58c2ecf20Sopenharmony_ci * @remark Read the file COPYING
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * @author John Levon <levon@movementarian.org>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/notifier.h>
128c2ecf20Sopenharmony_ci#include <linux/smp.h>
138c2ecf20Sopenharmony_ci#include <linux/oprofile.h>
148c2ecf20Sopenharmony_ci#include <linux/profile.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/cpu.h>
178c2ecf20Sopenharmony_ci#include <linux/hrtimer.h>
188c2ecf20Sopenharmony_ci#include <asm/irq_regs.h>
198c2ecf20Sopenharmony_ci#include <asm/ptrace.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "oprof.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct hrtimer, oprofile_hrtimer);
248c2ecf20Sopenharmony_cistatic int ctr_running;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic enum hrtimer_restart oprofile_hrtimer_notify(struct hrtimer *hrtimer)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	oprofile_add_sample(get_irq_regs(), 0);
298c2ecf20Sopenharmony_ci	hrtimer_forward_now(hrtimer, ns_to_ktime(TICK_NSEC));
308c2ecf20Sopenharmony_ci	return HRTIMER_RESTART;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic void __oprofile_hrtimer_start(void *unused)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct hrtimer *hrtimer = this_cpu_ptr(&oprofile_hrtimer);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	if (!ctr_running)
388c2ecf20Sopenharmony_ci		return;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
418c2ecf20Sopenharmony_ci	hrtimer->function = oprofile_hrtimer_notify;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	hrtimer_start(hrtimer, ns_to_ktime(TICK_NSEC),
448c2ecf20Sopenharmony_ci		      HRTIMER_MODE_REL_PINNED);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic int oprofile_hrtimer_start(void)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	get_online_cpus();
508c2ecf20Sopenharmony_ci	ctr_running = 1;
518c2ecf20Sopenharmony_ci	on_each_cpu(__oprofile_hrtimer_start, NULL, 1);
528c2ecf20Sopenharmony_ci	put_online_cpus();
538c2ecf20Sopenharmony_ci	return 0;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic void __oprofile_hrtimer_stop(int cpu)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct hrtimer *hrtimer = &per_cpu(oprofile_hrtimer, cpu);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (!ctr_running)
618c2ecf20Sopenharmony_ci		return;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	hrtimer_cancel(hrtimer);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void oprofile_hrtimer_stop(void)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	int cpu;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	get_online_cpus();
718c2ecf20Sopenharmony_ci	for_each_online_cpu(cpu)
728c2ecf20Sopenharmony_ci		__oprofile_hrtimer_stop(cpu);
738c2ecf20Sopenharmony_ci	ctr_running = 0;
748c2ecf20Sopenharmony_ci	put_online_cpus();
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int oprofile_timer_online(unsigned int cpu)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	local_irq_disable();
808c2ecf20Sopenharmony_ci	__oprofile_hrtimer_start(NULL);
818c2ecf20Sopenharmony_ci	local_irq_enable();
828c2ecf20Sopenharmony_ci	return 0;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int oprofile_timer_prep_down(unsigned int cpu)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	__oprofile_hrtimer_stop(cpu);
888c2ecf20Sopenharmony_ci	return 0;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic enum cpuhp_state hp_online;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int oprofile_hrtimer_setup(void)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	int ret;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
988c2ecf20Sopenharmony_ci					"oprofile/timer:online",
998c2ecf20Sopenharmony_ci					oprofile_timer_online,
1008c2ecf20Sopenharmony_ci					oprofile_timer_prep_down);
1018c2ecf20Sopenharmony_ci	if (ret < 0)
1028c2ecf20Sopenharmony_ci		return ret;
1038c2ecf20Sopenharmony_ci	hp_online = ret;
1048c2ecf20Sopenharmony_ci	return 0;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic void oprofile_hrtimer_shutdown(void)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	cpuhp_remove_state_nocalls(hp_online);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ciint oprofile_timer_init(struct oprofile_operations *ops)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	ops->create_files	= NULL;
1158c2ecf20Sopenharmony_ci	ops->setup		= oprofile_hrtimer_setup;
1168c2ecf20Sopenharmony_ci	ops->shutdown		= oprofile_hrtimer_shutdown;
1178c2ecf20Sopenharmony_ci	ops->start		= oprofile_hrtimer_start;
1188c2ecf20Sopenharmony_ci	ops->stop		= oprofile_hrtimer_stop;
1198c2ecf20Sopenharmony_ci	ops->cpu_type		= "timer";
1208c2ecf20Sopenharmony_ci	printk(KERN_INFO "oprofile: using timer interrupt.\n");
1218c2ecf20Sopenharmony_ci	return 0;
1228c2ecf20Sopenharmony_ci}
123