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 "gator.h"
11 #include <linux/slab.h>
12 #include <linux/cpufreq.h>
13 #include <trace/events/power.h>
15 // cpu_frequency and cpu_idle trace points were introduced in Linux kernel v2.6.38
16 // the now deprecated power_frequency trace point was available prior to 2.6.38, but only for x86
17 // CPU Idle is currently disabled in the .xml
18 #if GATOR_CPU_FREQ_SUPPORT
19 enum {
20 POWER_CPU_FREQ,
21 POWER_CPU_IDLE,
22 POWER_TOTAL
23 };
25 static ulong power_cpu_enabled[POWER_TOTAL];
26 static ulong power_cpu_key[POWER_TOTAL];
27 static DEFINE_PER_CPU(ulong[POWER_TOTAL], power);
28 static DEFINE_PER_CPU(ulong[POWER_TOTAL], prev);
29 static DEFINE_PER_CPU(int *, powerGet);
31 GATOR_DEFINE_PROBE(cpu_frequency, TP_PROTO(unsigned int frequency, unsigned int cpu))
32 {
33 per_cpu(power, cpu)[POWER_CPU_FREQ] = frequency * 1000;
34 }
36 GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu))
37 {
38 per_cpu(power, cpu)[POWER_CPU_IDLE] = state;
39 }
41 static int gator_events_power_create_files(struct super_block *sb, struct dentry *root)
42 {
43 struct dentry *dir;
45 // cpu_frequency
46 dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_freq");
47 if (!dir) {
48 return -1;
49 }
50 gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_FREQ]);
51 gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_FREQ]);
53 // cpu_idle
54 dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_idle");
55 if (!dir) {
56 return -1;
57 }
58 gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_IDLE]);
59 gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_IDLE]);
61 return 0;
62 }
64 static int gator_events_power_populate(int cpu, int** buffer)
65 {
66 int i, len = 0;
68 for (i = 0; i < POWER_TOTAL; i++) {
69 if (power_cpu_enabled[i]) {
70 if (per_cpu(power, cpu)[i] != per_cpu(prev, cpu)[i]) {
71 per_cpu(prev, cpu)[i] = per_cpu(power, cpu)[i];
72 per_cpu(powerGet, cpu)[len++] = power_cpu_key[i];
73 per_cpu(powerGet, cpu)[len++] = per_cpu(power, cpu)[i];
74 }
75 }
76 }
78 if (buffer)
79 *buffer = per_cpu(powerGet, cpu);
81 return len;
82 }
84 static int gator_events_power_online(int** buffer)
85 {
86 int i, cpu = smp_processor_id();
87 for (i = 0; i < POWER_TOTAL; i++)
88 per_cpu(prev, cpu)[i] = -1;
89 per_cpu(power, cpu)[POWER_CPU_FREQ] = cpufreq_quick_get(cpu) * 1000;
90 return gator_events_power_populate(cpu, buffer);
91 }
93 static int gator_events_power_offline(int** buffer)
94 {
95 int cpu = smp_processor_id();
96 // Set frequency to zero on an offline
97 per_cpu(power, cpu)[POWER_CPU_FREQ] = 0;
98 return gator_events_power_populate(cpu, buffer);
99 }
101 static int gator_events_power_start(void)
102 {
103 int cpu;
105 for_each_present_cpu(cpu) {
106 per_cpu(powerGet, cpu) = kmalloc(POWER_TOTAL * 2, GFP_KERNEL);
107 if (!per_cpu(powerGet, cpu))
108 return -1;
109 }
111 // register tracepoints
112 if (power_cpu_enabled[POWER_CPU_FREQ])
113 if (GATOR_REGISTER_TRACE(cpu_frequency))
114 goto fail_cpu_frequency_exit;
115 if (power_cpu_enabled[POWER_CPU_IDLE])
116 if (GATOR_REGISTER_TRACE(cpu_idle))
117 goto fail_cpu_idle_exit;
118 pr_debug("gator: registered power event tracepoints\n");
120 return 0;
122 // unregister tracepoints on error
123 fail_cpu_idle_exit:
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_events_power_stop(void)
132 {
133 int i, cpu;
134 if (power_cpu_enabled[POWER_CPU_FREQ])
135 GATOR_UNREGISTER_TRACE(cpu_frequency);
136 if (power_cpu_enabled[POWER_CPU_IDLE])
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 }
144 for_each_present_cpu(cpu) {
145 kfree(per_cpu(powerGet, cpu));
146 }
147 }
149 static int gator_events_power_read(int **buffer)
150 {
151 return gator_events_power_populate(smp_processor_id(), buffer);
152 }
154 static struct gator_interface gator_events_power_interface = {
155 .create_files = gator_events_power_create_files,
156 .online = gator_events_power_online,
157 .offline = gator_events_power_offline,
158 .start = gator_events_power_start,
159 .stop = gator_events_power_stop,
160 .read = gator_events_power_read,
161 };
162 #endif
164 int gator_events_power_init(void)
165 {
166 #if (GATOR_CPU_FREQ_SUPPORT)
167 int i;
168 for (i = 0; i < POWER_TOTAL; i++) {
169 power_cpu_enabled[i] = 0;
170 power_cpu_key[i] = gator_events_get_key();
171 }
173 return gator_events_install(&gator_events_power_interface);
174 #else
175 return -1;
176 #endif
177 }
178 gator_events_init(gator_events_power_init);