1 /**
2 * Copyright (C) ARM Limited 2011-2012. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
10 #include <linux/cpufreq.h>
11 #include <trace/events/power.h>
13 #if defined(__arm__)
15 #include <asm/mach-types.h>
17 #define implements_wfi() (!machine_is_omap3_beagle())
19 #else
21 #define implements_wfi() false
23 #endif
25 // cpu_frequency and cpu_idle trace points were introduced in Linux kernel v2.6.38
26 // the now deprecated power_frequency trace point was available prior to 2.6.38, but only for x86
27 #if GATOR_CPU_FREQ_SUPPORT
28 enum {
29 POWER_CPU_FREQ,
30 POWER_CPU_IDLE,
31 POWER_TOTAL
32 };
34 static DEFINE_PER_CPU(ulong, idle_prev_state);
35 static ulong power_cpu_enabled[POWER_TOTAL];
36 static ulong power_cpu_key[POWER_TOTAL];
38 static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root)
39 {
40 struct dentry *dir;
42 // cpu_frequency
43 dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_freq");
44 if (!dir) {
45 return -1;
46 }
47 gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_FREQ]);
48 gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_FREQ]);
50 // cpu_idle
51 dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_idle");
52 if (!dir) {
53 return -1;
54 }
55 gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_IDLE]);
56 gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_IDLE]);
58 return 0;
59 }
61 // 'cpu' may not equal smp_processor_id(), i.e. may not be running on the core that is having the freq/idle state change
62 GATOR_DEFINE_PROBE(cpu_frequency, TP_PROTO(unsigned int frequency, unsigned int cpu))
63 {
64 marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], frequency * 1000);
65 }
67 #define WFI_EXIT 2
68 #define WFI_ENTER 1
69 GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu))
70 {
71 if (state == per_cpu(idle_prev_state, cpu)) {
72 return;
73 }
75 if (implements_wfi()) {
76 if (state == PWR_EVENT_EXIT) {
77 // transition from wfi to non-wfi
78 marshal_idle(cpu, WFI_EXIT);
79 } else {
80 // transition from non-wfi to wfi
81 marshal_idle(cpu, WFI_ENTER);
82 }
83 }
85 per_cpu(idle_prev_state, cpu) = state;
87 if (power_cpu_enabled[POWER_CPU_IDLE]) {
88 // Increment state so that no negative numbers are sent
89 marshal_event_single(cpu, power_cpu_key[POWER_CPU_IDLE], state + 1);
90 }
91 }
93 static void gator_trace_power_online(void)
94 {
95 int cpu = smp_processor_id();
96 if (power_cpu_enabled[POWER_CPU_FREQ]) {
97 marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(cpu) * 1000);
98 }
99 }
101 static void gator_trace_power_offline(void)
102 {
103 // Set frequency to zero on an offline
104 int cpu = smp_processor_id();
105 if (power_cpu_enabled[POWER_CPU_FREQ]) {
106 marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], 0);
107 }
108 }
110 static int gator_trace_power_start(void)
111 {
112 int cpu;
114 // register tracepoints
115 if (power_cpu_enabled[POWER_CPU_FREQ])
116 if (GATOR_REGISTER_TRACE(cpu_frequency))
117 goto fail_cpu_frequency_exit;
119 // Always register for cpu:idle for detecting WFI, independent of power_cpu_enabled[POWER_CPU_IDLE]
120 if (GATOR_REGISTER_TRACE(cpu_idle))
121 goto fail_cpu_idle_exit;
122 pr_debug("gator: registered power event tracepoints\n");
124 for_each_present_cpu(cpu) {
125 per_cpu(idle_prev_state, cpu) = 0;
126 }
128 return 0;
130 // unregister tracepoints on error
131 fail_cpu_idle_exit:
132 if (power_cpu_enabled[POWER_CPU_FREQ])
133 GATOR_UNREGISTER_TRACE(cpu_frequency);
134 fail_cpu_frequency_exit:
135 pr_err("gator: power event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
137 return -1;
138 }
140 static void gator_trace_power_stop(void)
141 {
142 int i;
144 if (power_cpu_enabled[POWER_CPU_FREQ])
145 GATOR_UNREGISTER_TRACE(cpu_frequency);
146 GATOR_UNREGISTER_TRACE(cpu_idle);
147 pr_debug("gator: unregistered power event tracepoints\n");
149 for (i = 0; i < POWER_TOTAL; i++) {
150 power_cpu_enabled[i] = 0;
151 }
152 }
154 void gator_trace_power_init(void)
155 {
156 int i;
157 for (i = 0; i < POWER_TOTAL; i++) {
158 power_cpu_enabled[i] = 0;
159 power_cpu_key[i] = gator_events_get_key();
160 }
161 }
162 #else
163 static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root)
164 {
165 return 0;
166 }
168 static void gator_trace_power_online(void)
169 {
170 }
172 static void gator_trace_power_offline(void)
173 {
174 }
176 static int gator_trace_power_start(void)
177 {
178 return 0;
179 }
181 static void gator_trace_power_stop(void)
182 {
183 }
185 void gator_trace_power_init(void)
186 {
187 }
188 #endif