Merge branch 'fixes' of git://git.armlinux.org.uk/~rmk/linux-arm
[platform/kernel/linux-exynos.git] / drivers / oprofile / nmi_timer_int.c
1 // SPDX-License-Identifier: GPL-2.0
2 /**
3  * @file nmi_timer_int.c
4  *
5  * @remark Copyright 2011 Advanced Micro Devices, Inc.
6  *
7  * @author Robert Richter <robert.richter@amd.com>
8  */
9
10 #include <linux/init.h>
11 #include <linux/smp.h>
12 #include <linux/errno.h>
13 #include <linux/oprofile.h>
14 #include <linux/perf_event.h>
15
16 #ifdef CONFIG_OPROFILE_NMI_TIMER
17
18 static DEFINE_PER_CPU(struct perf_event *, nmi_timer_events);
19 static int ctr_running;
20
21 static struct perf_event_attr nmi_timer_attr = {
22         .type           = PERF_TYPE_HARDWARE,
23         .config         = PERF_COUNT_HW_CPU_CYCLES,
24         .size           = sizeof(struct perf_event_attr),
25         .pinned         = 1,
26         .disabled       = 1,
27 };
28
29 static void nmi_timer_callback(struct perf_event *event,
30                                struct perf_sample_data *data,
31                                struct pt_regs *regs)
32 {
33         event->hw.interrupts = 0;       /* don't throttle interrupts */
34         oprofile_add_sample(regs, 0);
35 }
36
37 static int nmi_timer_start_cpu(int cpu)
38 {
39         struct perf_event *event = per_cpu(nmi_timer_events, cpu);
40
41         if (!event) {
42                 event = perf_event_create_kernel_counter(&nmi_timer_attr, cpu, NULL,
43                                                          nmi_timer_callback, NULL);
44                 if (IS_ERR(event))
45                         return PTR_ERR(event);
46                 per_cpu(nmi_timer_events, cpu) = event;
47         }
48
49         if (event && ctr_running)
50                 perf_event_enable(event);
51
52         return 0;
53 }
54
55 static void nmi_timer_stop_cpu(int cpu)
56 {
57         struct perf_event *event = per_cpu(nmi_timer_events, cpu);
58
59         if (event && ctr_running)
60                 perf_event_disable(event);
61 }
62
63 static int nmi_timer_cpu_online(unsigned int cpu)
64 {
65         nmi_timer_start_cpu(cpu);
66         return 0;
67 }
68 static int nmi_timer_cpu_predown(unsigned int cpu)
69 {
70         nmi_timer_stop_cpu(cpu);
71         return 0;
72 }
73
74 static int nmi_timer_start(void)
75 {
76         int cpu;
77
78         get_online_cpus();
79         ctr_running = 1;
80         for_each_online_cpu(cpu)
81                 nmi_timer_start_cpu(cpu);
82         put_online_cpus();
83
84         return 0;
85 }
86
87 static void nmi_timer_stop(void)
88 {
89         int cpu;
90
91         get_online_cpus();
92         for_each_online_cpu(cpu)
93                 nmi_timer_stop_cpu(cpu);
94         ctr_running = 0;
95         put_online_cpus();
96 }
97
98 static enum cpuhp_state hp_online;
99
100 static void nmi_timer_shutdown(void)
101 {
102         struct perf_event *event;
103         int cpu;
104
105         cpuhp_remove_state(hp_online);
106         for_each_possible_cpu(cpu) {
107                 event = per_cpu(nmi_timer_events, cpu);
108                 if (!event)
109                         continue;
110                 perf_event_disable(event);
111                 per_cpu(nmi_timer_events, cpu) = NULL;
112                 perf_event_release_kernel(event);
113         }
114 }
115
116 static int nmi_timer_setup(void)
117 {
118         int err;
119         u64 period;
120
121         /* clock cycles per tick: */
122         period = (u64)cpu_khz * 1000;
123         do_div(period, HZ);
124         nmi_timer_attr.sample_period = period;
125
126         err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "oprofile/nmi:online",
127                                 nmi_timer_cpu_online, nmi_timer_cpu_predown);
128         if (err < 0) {
129                 nmi_timer_shutdown();
130                 return err;
131         }
132         hp_online = err;
133         return 0;
134 }
135
136 int __init op_nmi_timer_init(struct oprofile_operations *ops)
137 {
138         int err = 0;
139
140         err = nmi_timer_setup();
141         if (err)
142                 return err;
143         nmi_timer_shutdown();           /* only check, don't alloc */
144
145         ops->create_files       = NULL;
146         ops->setup              = nmi_timer_setup;
147         ops->shutdown           = nmi_timer_shutdown;
148         ops->start              = nmi_timer_start;
149         ops->stop               = nmi_timer_stop;
150         ops->cpu_type           = "timer";
151
152         printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
153
154         return 0;
155 }
156
157 #endif