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>
12 #include <asm/mach-types.h>
14 // cpu_frequency and cpu_idle trace points were introduced in Linux kernel v2.6.38
15 // the now deprecated power_frequency trace point was available prior to 2.6.38, but only for x86
16 #if GATOR_CPU_FREQ_SUPPORT
17 enum {
18 POWER_CPU_FREQ,
19 POWER_CPU_IDLE,
20 POWER_TOTAL
21 };
23 static DEFINE_PER_CPU(ulong, idle_prev_state);
24 static ulong power_cpu_enabled[POWER_TOTAL];
25 static ulong power_cpu_key[POWER_TOTAL];
27 static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root)
28 {
29 struct dentry *dir;
31 // cpu_frequency
32 dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_freq");
33 if (!dir) {
34 return -1;
35 }
36 gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_FREQ]);
37 gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_FREQ]);
39 // cpu_idle
40 dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_idle");
41 if (!dir) {
42 return -1;
43 }
44 gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_IDLE]);
45 gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_IDLE]);
47 return 0;
48 }
50 // 'cpu' may not equal smp_processor_id(), i.e. may not be running on the core that is having the freq/idle state change
51 GATOR_DEFINE_PROBE(cpu_frequency, TP_PROTO(unsigned int frequency, unsigned int cpu))
52 {
53 marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], frequency * 1000);
54 }
56 #define WFI_EXIT 2
57 #define WFI_ENTER 1
58 GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu))
59 {
60 if (state == per_cpu(idle_prev_state, cpu)) {
61 return;
62 }
64 if (!machine_is_omap3_beagle()) {
65 if (state == PWR_EVENT_EXIT) {
66 // transition from wfi to non-wfi
67 marshal_idle(cpu, WFI_EXIT);
68 } else {
69 // transition from non-wfi to wfi
70 marshal_idle(cpu, WFI_ENTER);
71 }
72 }
74 per_cpu(idle_prev_state, cpu) = state;
76 if (power_cpu_enabled[POWER_CPU_IDLE]) {
77 // Increment state so that no negative numbers are sent
78 marshal_event_single(cpu, power_cpu_key[POWER_CPU_IDLE], state + 1);
79 }
80 }
82 static void gator_trace_power_online(void)
83 {
84 int cpu = smp_processor_id();
85 if (power_cpu_enabled[POWER_CPU_FREQ]) {
86 marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(cpu) * 1000);
87 }
88 }
90 static void gator_trace_power_offline(void)
91 {
92 // Set frequency to zero on an offline
93 int cpu = smp_processor_id();
94 if (power_cpu_enabled[POWER_CPU_FREQ]) {
95 marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], 0);
96 }
97 }
99 static int gator_trace_power_start(void)
100 {
101 int cpu;
103 // register tracepoints
104 if (power_cpu_enabled[POWER_CPU_FREQ])
105 if (GATOR_REGISTER_TRACE(cpu_frequency))
106 goto fail_cpu_frequency_exit;
108 // Always register for cpu:idle for detecting WFI, independent of power_cpu_enabled[POWER_CPU_IDLE]
109 if (GATOR_REGISTER_TRACE(cpu_idle))
110 goto fail_cpu_idle_exit;
111 pr_debug("gator: registered power event tracepoints\n");
113 for_each_present_cpu(cpu) {
114 per_cpu(idle_prev_state, cpu) = 0;
115 }
117 return 0;
119 // unregister tracepoints on error
120 fail_cpu_idle_exit:
121 if (power_cpu_enabled[POWER_CPU_FREQ])
122 GATOR_UNREGISTER_TRACE(cpu_frequency);
123 fail_cpu_frequency_exit:
124 pr_err("gator: power event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
126 return -1;
127 }
129 static void gator_trace_power_stop(void)
130 {
131 int i;
133 if (power_cpu_enabled[POWER_CPU_FREQ])
134 GATOR_UNREGISTER_TRACE(cpu_frequency);
135 GATOR_UNREGISTER_TRACE(cpu_idle);
136 pr_debug("gator: unregistered power event tracepoints\n");
138 for (i = 0; i < POWER_TOTAL; i++) {
139 power_cpu_enabled[i] = 0;
140 }
141 }
143 void gator_trace_power_init(void)
144 {
145 int i;
146 for (i = 0; i < POWER_TOTAL; i++) {
147 power_cpu_enabled[i] = 0;
148 power_cpu_key[i] = gator_events_get_key();
149 }
150 }
151 #else
152 static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root) {return 0;}
153 static void gator_trace_power_online(void) {}
154 static void gator_trace_power_offline(void) {}
155 static int gator_trace_power_start(void) {return 0;}
156 static void gator_trace_power_stop(void) {}
157 void gator_trace_power_init(void) {}
158 #endif