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 // cpu_frequency and cpu_idle trace points were introduced in Linux kernel v2.6.38
14 // the now deprecated power_frequency trace point was available prior to 2.6.38, but only for x86
15 #if GATOR_CPU_FREQ_SUPPORT
16 enum {
17 POWER_CPU_FREQ,
18 POWER_CPU_IDLE,
19 POWER_TOTAL
20 };
22 static DEFINE_PER_CPU(ulong, idle_prev_state);
23 static ulong power_cpu_enabled[POWER_TOTAL];
24 static ulong power_cpu_key[POWER_TOTAL];
26 static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root)
27 {
28 struct dentry *dir;
30 // cpu_frequency
31 dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_freq");
32 if (!dir) {
33 return -1;
34 }
35 gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_FREQ]);
36 gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_FREQ]);
38 // cpu_idle
39 dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_idle");
40 if (!dir) {
41 return -1;
42 }
43 gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_IDLE]);
44 gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_IDLE]);
46 return 0;
47 }
49 // 'cpu' may not equal smp_processor_id(), i.e. may not be running on the core that is having the freq/idle state change
50 GATOR_DEFINE_PROBE(cpu_frequency, TP_PROTO(unsigned int frequency, unsigned int cpu))
51 {
52 marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], frequency * 1000);
53 }
55 #define WFI_ACTIVE_THRESHOLD 2 // may vary on platform/OS
56 #define WFI_EXIT 0
57 #define WFI_ENTER 1
58 GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu))
59 {
60 // the streamline engine treats all counter values as unsigned
61 if (state & 0x80000000) {
62 state = 0;
63 }
65 if (state == per_cpu(idle_prev_state, cpu)) {
66 return;
67 }
69 if (state < WFI_ACTIVE_THRESHOLD && per_cpu(idle_prev_state, cpu) >= WFI_ACTIVE_THRESHOLD) {
70 // transition from wfi to non-wfi
71 marshal_wfi(cpu, WFI_EXIT);
72 } else if (state >= WFI_ACTIVE_THRESHOLD && per_cpu(idle_prev_state, cpu) < WFI_ACTIVE_THRESHOLD) {
73 // transition from non-wfi to wfi
74 marshal_wfi(cpu, WFI_ENTER);
75 }
77 per_cpu(idle_prev_state, cpu) = state;
79 if (power_cpu_enabled[POWER_CPU_IDLE]) {
80 marshal_event_single(cpu, power_cpu_key[POWER_CPU_IDLE], state);
81 }
82 }
84 static void gator_trace_power_online(void)
85 {
86 int cpu = smp_processor_id();
87 if (power_cpu_enabled[POWER_CPU_FREQ]) {
88 marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(cpu) * 1000);
89 }
90 }
92 static void gator_trace_power_offline(void)
93 {
94 // Set frequency to zero on an offline
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], 0);
98 }
99 }
101 static int gator_trace_power_start(void)
102 {
103 int cpu;
105 // register tracepoints
106 if (power_cpu_enabled[POWER_CPU_FREQ])
107 if (GATOR_REGISTER_TRACE(cpu_frequency))
108 goto fail_cpu_frequency_exit;
110 // Always register for cpu:idle for detecting WFI, independent of power_cpu_enabled[POWER_CPU_IDLE]
111 if (GATOR_REGISTER_TRACE(cpu_idle))
112 goto fail_cpu_idle_exit;
113 pr_debug("gator: registered power event tracepoints\n");
115 for_each_present_cpu(cpu) {
116 per_cpu(idle_prev_state, cpu) = 0;
117 }
119 return 0;
121 // unregister tracepoints on error
122 fail_cpu_idle_exit:
123 if (power_cpu_enabled[POWER_CPU_FREQ])
124 GATOR_UNREGISTER_TRACE(cpu_frequency);
125 fail_cpu_frequency_exit:
126 pr_err("gator: power event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
128 return -1;
129 }
131 static void gator_trace_power_stop(void)
132 {
133 int i;
135 if (power_cpu_enabled[POWER_CPU_FREQ])
136 GATOR_UNREGISTER_TRACE(cpu_frequency);
137 GATOR_UNREGISTER_TRACE(cpu_idle);
138 pr_debug("gator: unregistered power event tracepoints\n");
140 for (i = 0; i < POWER_TOTAL; i++) {
141 power_cpu_enabled[i] = 0;
142 }
143 }
145 void gator_trace_power_init(void)
146 {
147 int i;
148 for (i = 0; i < POWER_TOTAL; i++) {
149 power_cpu_enabled[i] = 0;
150 power_cpu_key[i] = gator_events_get_key();
151 }
152 }
153 #else
154 static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root) {return 0;}
155 static void gator_trace_power_online(void) {}
156 static void gator_trace_power_offline(void) {}
157 static int gator_trace_power_start(void) {return 0;}
158 static void gator_trace_power_stop(void) {}
159 void gator_trace_power_init(void) {}
160 #endif