| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 1 | /* | 
|  | 2 | *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de> | 
|  | 3 | * | 
|  | 4 | *  Licensed under the terms of the GNU GPL License version 2. | 
|  | 5 | */ | 
|  | 6 |  | 
|  | 7 |  | 
|  | 8 | #include <unistd.h> | 
|  | 9 | #include <stdio.h> | 
|  | 10 | #include <errno.h> | 
|  | 11 | #include <stdlib.h> | 
|  | 12 | #include <string.h> | 
|  | 13 |  | 
|  | 14 | #include <getopt.h> | 
|  | 15 |  | 
|  | 16 | #include "cpufreq.h" | 
|  | 17 | #include "helpers/helpers.h" | 
|  | 18 | #include "helpers/bitmask.h" | 
|  | 19 |  | 
|  | 20 | #define LINE_LEN 10 | 
|  | 21 |  | 
|  | 22 | static unsigned int count_cpus(void) | 
|  | 23 | { | 
|  | 24 | FILE *fp; | 
|  | 25 | char value[LINE_LEN]; | 
|  | 26 | unsigned int ret = 0; | 
|  | 27 | unsigned int cpunr = 0; | 
|  | 28 |  | 
|  | 29 | fp = fopen("/proc/stat", "r"); | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 30 | if (!fp) { | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 31 | printf(_("Couldn't count the number of CPUs (%s: %s), assuming 1\n"), "/proc/stat", strerror(errno)); | 
|  | 32 | return 1; | 
|  | 33 | } | 
|  | 34 |  | 
|  | 35 | while (!feof(fp)) { | 
|  | 36 | if (!fgets(value, LINE_LEN, fp)) | 
|  | 37 | continue; | 
|  | 38 | value[LINE_LEN - 1] = '\0'; | 
|  | 39 | if (strlen(value) < (LINE_LEN - 2)) | 
|  | 40 | continue; | 
|  | 41 | if (strstr(value, "cpu ")) | 
|  | 42 | continue; | 
|  | 43 | if (sscanf(value, "cpu%d ", &cpunr) != 1) | 
|  | 44 | continue; | 
|  | 45 | if (cpunr > ret) | 
|  | 46 | ret = cpunr; | 
|  | 47 | } | 
|  | 48 | fclose(fp); | 
|  | 49 |  | 
|  | 50 | /* cpu count starts from 0, on error return 1 (UP) */ | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 51 | return ret + 1; | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 52 | } | 
|  | 53 |  | 
|  | 54 |  | 
|  | 55 | static void proc_cpufreq_output(void) | 
|  | 56 | { | 
|  | 57 | unsigned int cpu, nr_cpus; | 
|  | 58 | struct cpufreq_policy *policy; | 
|  | 59 | unsigned int min_pctg = 0; | 
|  | 60 | unsigned int max_pctg = 0; | 
|  | 61 | unsigned long min, max; | 
|  | 62 |  | 
|  | 63 | printf(_("          minimum CPU frequency  -  maximum CPU frequency  -  governor\n")); | 
|  | 64 |  | 
|  | 65 | nr_cpus = count_cpus(); | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 66 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 67 | policy = cpufreq_get_policy(cpu); | 
|  | 68 | if (!policy) | 
|  | 69 | continue; | 
|  | 70 |  | 
|  | 71 | if (cpufreq_get_hardware_limits(cpu, &min, &max)) { | 
|  | 72 | max = 0; | 
|  | 73 | } else { | 
|  | 74 | min_pctg = (policy->min * 100) / max; | 
|  | 75 | max_pctg = (policy->max * 100) / max; | 
|  | 76 | } | 
|  | 77 | printf("CPU%3d    %9lu kHz (%3d %%)  -  %9lu kHz (%3d %%)  -  %s\n", | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 78 | cpu , policy->min, max ? min_pctg : 0, policy->max, | 
|  | 79 | max ? max_pctg : 0, policy->governor); | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 80 |  | 
|  | 81 | cpufreq_put_policy(policy); | 
|  | 82 | } | 
|  | 83 | } | 
|  | 84 |  | 
|  | 85 | static void print_speed(unsigned long speed) | 
|  | 86 | { | 
|  | 87 | unsigned long tmp; | 
|  | 88 |  | 
|  | 89 | if (speed > 1000000) { | 
|  | 90 | tmp = speed % 10000; | 
|  | 91 | if (tmp >= 5000) | 
|  | 92 | speed += 10000; | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 93 | printf("%u.%02u GHz", ((unsigned int) speed/1000000), | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 94 | ((unsigned int) (speed%1000000)/10000)); | 
|  | 95 | } else if (speed > 100000) { | 
|  | 96 | tmp = speed % 1000; | 
|  | 97 | if (tmp >= 500) | 
|  | 98 | speed += 1000; | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 99 | printf("%u MHz", ((unsigned int) speed / 1000)); | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 100 | } else if (speed > 1000) { | 
|  | 101 | tmp = speed % 100; | 
|  | 102 | if (tmp >= 50) | 
|  | 103 | speed += 100; | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 104 | printf("%u.%01u MHz", ((unsigned int) speed/1000), | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 105 | ((unsigned int) (speed%1000)/100)); | 
|  | 106 | } else | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 107 | printf("%lu kHz", speed); | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 108 |  | 
|  | 109 | return; | 
|  | 110 | } | 
|  | 111 |  | 
|  | 112 | static void print_duration(unsigned long duration) | 
|  | 113 | { | 
|  | 114 | unsigned long tmp; | 
|  | 115 |  | 
|  | 116 | if (duration > 1000000) { | 
|  | 117 | tmp = duration % 10000; | 
|  | 118 | if (tmp >= 5000) | 
|  | 119 | duration += 10000; | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 120 | printf("%u.%02u ms", ((unsigned int) duration/1000000), | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 121 | ((unsigned int) (duration%1000000)/10000)); | 
|  | 122 | } else if (duration > 100000) { | 
|  | 123 | tmp = duration % 1000; | 
|  | 124 | if (tmp >= 500) | 
|  | 125 | duration += 1000; | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 126 | printf("%u us", ((unsigned int) duration / 1000)); | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 127 | } else if (duration > 1000) { | 
|  | 128 | tmp = duration % 100; | 
|  | 129 | if (tmp >= 50) | 
|  | 130 | duration += 100; | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 131 | printf("%u.%01u us", ((unsigned int) duration/1000), | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 132 | ((unsigned int) (duration%1000)/100)); | 
|  | 133 | } else | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 134 | printf("%lu ns", duration); | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 135 |  | 
|  | 136 | return; | 
|  | 137 | } | 
|  | 138 |  | 
|  | 139 | /* --boost / -b */ | 
|  | 140 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 141 | static int get_boost_mode(unsigned int cpu) | 
|  | 142 | { | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 143 | int support, active, b_states = 0, ret, pstate_no, i; | 
|  | 144 | /* ToDo: Make this more global */ | 
|  | 145 | unsigned long pstates[MAX_HW_PSTATES] = {0,}; | 
|  | 146 |  | 
|  | 147 | if (cpupower_cpu_info.vendor != X86_VENDOR_AMD && | 
|  | 148 | cpupower_cpu_info.vendor != X86_VENDOR_INTEL) | 
|  | 149 | return 0; | 
|  | 150 |  | 
|  | 151 | ret = cpufreq_has_boost_support(cpu, &support, &active, &b_states); | 
|  | 152 | if (ret) { | 
|  | 153 | printf(_("Error while evaluating Boost Capabilities" | 
|  | 154 | " on CPU %d -- are you root?\n"), cpu); | 
|  | 155 | return ret; | 
|  | 156 | } | 
|  | 157 | /* P state changes via MSR are identified via cpuid 80000007 | 
|  | 158 | on Intel and AMD, but we assume boost capable machines can do that | 
|  | 159 | if (cpuid_eax(0x80000000) >= 0x80000007 | 
|  | 160 | && (cpuid_edx(0x80000007) & (1 << 7))) | 
|  | 161 | */ | 
|  | 162 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 163 | printf(_("  boost state support:\n")); | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 164 |  | 
|  | 165 | printf(_("    Supported: %s\n"), support ? _("yes") : _("no")); | 
|  | 166 | printf(_("    Active: %s\n"), active ? _("yes") : _("no")); | 
|  | 167 |  | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 168 | if (cpupower_cpu_info.vendor == X86_VENDOR_AMD && | 
|  | 169 | cpupower_cpu_info.family >= 0x10) { | 
|  | 170 | ret = decode_pstates(cpu, cpupower_cpu_info.family, b_states, | 
|  | 171 | pstates, &pstate_no); | 
|  | 172 | if (ret) | 
|  | 173 | return ret; | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 174 |  | 
| Thomas Renninger | 8fb2e44 | 2011-07-21 11:54:53 +0200 | [diff] [blame] | 175 | printf(_("    Boost States: %d\n"), b_states); | 
|  | 176 | printf(_("    Total States: %d\n"), pstate_no); | 
|  | 177 | for (i = 0; i < pstate_no; i++) { | 
|  | 178 | if (i < b_states) | 
|  | 179 | printf(_("    Pstate-Pb%d: %luMHz (boost state)" | 
|  | 180 | "\n"), i, pstates[i]); | 
|  | 181 | else | 
|  | 182 | printf(_("    Pstate-P%d:  %luMHz\n"), | 
|  | 183 | i - b_states, pstates[i]); | 
|  | 184 | } | 
|  | 185 | } else if (cpupower_cpu_info.caps & CPUPOWER_CAP_HAS_TURBO_RATIO) { | 
|  | 186 | double bclk; | 
|  | 187 | unsigned long long intel_turbo_ratio = 0; | 
|  | 188 | unsigned int ratio; | 
|  | 189 |  | 
|  | 190 | /* Any way to autodetect this ? */ | 
|  | 191 | if (cpupower_cpu_info.caps & CPUPOWER_CAP_IS_SNB) | 
|  | 192 | bclk = 100.00; | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 193 | else | 
| Thomas Renninger | 8fb2e44 | 2011-07-21 11:54:53 +0200 | [diff] [blame] | 194 | bclk = 133.33; | 
|  | 195 | intel_turbo_ratio = msr_intel_get_turbo_ratio(cpu); | 
|  | 196 | dprint ("    Ratio: 0x%llx - bclk: %f\n", | 
|  | 197 | intel_turbo_ratio, bclk); | 
|  | 198 |  | 
|  | 199 | ratio = (intel_turbo_ratio >> 24) & 0xFF; | 
|  | 200 | if (ratio) | 
|  | 201 | printf(_("    %.0f MHz max turbo 4 active cores\n"), | 
|  | 202 | ratio * bclk); | 
|  | 203 |  | 
|  | 204 | ratio = (intel_turbo_ratio >> 16) & 0xFF; | 
|  | 205 | if (ratio) | 
|  | 206 | printf(_("    %.0f MHz max turbo 3 active cores\n"), | 
|  | 207 | ratio * bclk); | 
|  | 208 |  | 
|  | 209 | ratio = (intel_turbo_ratio >> 8) & 0xFF; | 
|  | 210 | if (ratio) | 
|  | 211 | printf(_("    %.0f MHz max turbo 2 active cores\n"), | 
|  | 212 | ratio * bclk); | 
|  | 213 |  | 
|  | 214 | ratio = (intel_turbo_ratio >> 0) & 0xFF; | 
|  | 215 | if (ratio) | 
|  | 216 | printf(_("    %.0f MHz max turbo 1 active cores\n"), | 
|  | 217 | ratio * bclk); | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 218 | } | 
|  | 219 | return 0; | 
|  | 220 | } | 
|  | 221 |  | 
|  | 222 | static void debug_output_one(unsigned int cpu) | 
|  | 223 | { | 
|  | 224 | char *driver; | 
|  | 225 | struct cpufreq_affected_cpus *cpus; | 
|  | 226 | struct cpufreq_available_frequencies *freqs; | 
|  | 227 | unsigned long min, max, freq_kernel, freq_hardware; | 
|  | 228 | unsigned long total_trans, latency; | 
|  | 229 | unsigned long long total_time; | 
|  | 230 | struct cpufreq_policy *policy; | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 231 | struct cpufreq_available_governors *governors; | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 232 | struct cpufreq_stats *stats; | 
|  | 233 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 234 | if (cpufreq_cpu_exists(cpu)) | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 235 | return; | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 236 |  | 
|  | 237 | freq_kernel = cpufreq_get_freq_kernel(cpu); | 
|  | 238 | freq_hardware = cpufreq_get_freq_hardware(cpu); | 
|  | 239 |  | 
|  | 240 | driver = cpufreq_get_driver(cpu); | 
|  | 241 | if (!driver) { | 
|  | 242 | printf(_("  no or unknown cpufreq driver is active on this CPU\n")); | 
|  | 243 | } else { | 
|  | 244 | printf(_("  driver: %s\n"), driver); | 
|  | 245 | cpufreq_put_driver(driver); | 
|  | 246 | } | 
|  | 247 |  | 
|  | 248 | cpus = cpufreq_get_related_cpus(cpu); | 
|  | 249 | if (cpus) { | 
|  | 250 | printf(_("  CPUs which run at the same hardware frequency: ")); | 
|  | 251 | while (cpus->next) { | 
|  | 252 | printf("%d ", cpus->cpu); | 
|  | 253 | cpus = cpus->next; | 
|  | 254 | } | 
|  | 255 | printf("%d\n", cpus->cpu); | 
|  | 256 | cpufreq_put_related_cpus(cpus); | 
|  | 257 | } | 
|  | 258 |  | 
|  | 259 | cpus = cpufreq_get_affected_cpus(cpu); | 
|  | 260 | if (cpus) { | 
|  | 261 | printf(_("  CPUs which need to have their frequency coordinated by software: ")); | 
|  | 262 | while (cpus->next) { | 
|  | 263 | printf("%d ", cpus->cpu); | 
|  | 264 | cpus = cpus->next; | 
|  | 265 | } | 
|  | 266 | printf("%d\n", cpus->cpu); | 
|  | 267 | cpufreq_put_affected_cpus(cpus); | 
|  | 268 | } | 
|  | 269 |  | 
|  | 270 | latency = cpufreq_get_transition_latency(cpu); | 
|  | 271 | if (latency) { | 
|  | 272 | printf(_("  maximum transition latency: ")); | 
|  | 273 | print_duration(latency); | 
|  | 274 | printf(".\n"); | 
|  | 275 | } | 
|  | 276 |  | 
|  | 277 | if (!(cpufreq_get_hardware_limits(cpu, &min, &max))) { | 
|  | 278 | printf(_("  hardware limits: ")); | 
|  | 279 | print_speed(min); | 
|  | 280 | printf(" - "); | 
|  | 281 | print_speed(max); | 
|  | 282 | printf("\n"); | 
|  | 283 | } | 
|  | 284 |  | 
|  | 285 | freqs = cpufreq_get_available_frequencies(cpu); | 
|  | 286 | if (freqs) { | 
|  | 287 | printf(_("  available frequency steps: ")); | 
|  | 288 | while (freqs->next) { | 
|  | 289 | print_speed(freqs->frequency); | 
|  | 290 | printf(", "); | 
|  | 291 | freqs = freqs->next; | 
|  | 292 | } | 
|  | 293 | print_speed(freqs->frequency); | 
|  | 294 | printf("\n"); | 
|  | 295 | cpufreq_put_available_frequencies(freqs); | 
|  | 296 | } | 
|  | 297 |  | 
|  | 298 | governors = cpufreq_get_available_governors(cpu); | 
|  | 299 | if (governors) { | 
|  | 300 | printf(_("  available cpufreq governors: ")); | 
|  | 301 | while (governors->next) { | 
|  | 302 | printf("%s, ", governors->governor); | 
|  | 303 | governors = governors->next; | 
|  | 304 | } | 
|  | 305 | printf("%s\n", governors->governor); | 
|  | 306 | cpufreq_put_available_governors(governors); | 
|  | 307 | } | 
|  | 308 |  | 
|  | 309 | policy = cpufreq_get_policy(cpu); | 
|  | 310 | if (policy) { | 
|  | 311 | printf(_("  current policy: frequency should be within ")); | 
|  | 312 | print_speed(policy->min); | 
|  | 313 | printf(_(" and ")); | 
|  | 314 | print_speed(policy->max); | 
|  | 315 |  | 
|  | 316 | printf(".\n                  "); | 
|  | 317 | printf(_("The governor \"%s\" may" | 
|  | 318 | " decide which speed to use\n                  within this range.\n"), | 
|  | 319 | policy->governor); | 
|  | 320 | cpufreq_put_policy(policy); | 
|  | 321 | } | 
|  | 322 |  | 
|  | 323 | if (freq_kernel || freq_hardware) { | 
|  | 324 | printf(_("  current CPU frequency is ")); | 
|  | 325 | if (freq_hardware) { | 
|  | 326 | print_speed(freq_hardware); | 
|  | 327 | printf(_(" (asserted by call to hardware)")); | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 328 | } else | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 329 | print_speed(freq_kernel); | 
|  | 330 | printf(".\n"); | 
|  | 331 | } | 
|  | 332 | stats = cpufreq_get_stats(cpu, &total_time); | 
|  | 333 | if (stats) { | 
|  | 334 | printf(_("  cpufreq stats: ")); | 
|  | 335 | while (stats) { | 
|  | 336 | print_speed(stats->frequency); | 
|  | 337 | printf(":%.2f%%", (100.0 * stats->time_in_state) / total_time); | 
|  | 338 | stats = stats->next; | 
|  | 339 | if (stats) | 
|  | 340 | printf(", "); | 
|  | 341 | } | 
|  | 342 | cpufreq_put_stats(stats); | 
|  | 343 | total_trans = cpufreq_get_transitions(cpu); | 
|  | 344 | if (total_trans) | 
|  | 345 | printf("  (%lu)\n", total_trans); | 
|  | 346 | else | 
|  | 347 | printf("\n"); | 
|  | 348 | } | 
|  | 349 | get_boost_mode(cpu); | 
|  | 350 |  | 
|  | 351 | } | 
|  | 352 |  | 
|  | 353 | /* --freq / -f */ | 
|  | 354 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 355 | static int get_freq_kernel(unsigned int cpu, unsigned int human) | 
|  | 356 | { | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 357 | unsigned long freq = cpufreq_get_freq_kernel(cpu); | 
|  | 358 | if (!freq) | 
|  | 359 | return -EINVAL; | 
|  | 360 | if (human) { | 
|  | 361 | print_speed(freq); | 
|  | 362 | printf("\n"); | 
|  | 363 | } else | 
|  | 364 | printf("%lu\n", freq); | 
|  | 365 | return 0; | 
|  | 366 | } | 
|  | 367 |  | 
|  | 368 |  | 
|  | 369 | /* --hwfreq / -w */ | 
|  | 370 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 371 | static int get_freq_hardware(unsigned int cpu, unsigned int human) | 
|  | 372 | { | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 373 | unsigned long freq = cpufreq_get_freq_hardware(cpu); | 
|  | 374 | if (!freq) | 
|  | 375 | return -EINVAL; | 
|  | 376 | if (human) { | 
|  | 377 | print_speed(freq); | 
|  | 378 | printf("\n"); | 
|  | 379 | } else | 
|  | 380 | printf("%lu\n", freq); | 
|  | 381 | return 0; | 
|  | 382 | } | 
|  | 383 |  | 
|  | 384 | /* --hwlimits / -l */ | 
|  | 385 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 386 | static int get_hardware_limits(unsigned int cpu) | 
|  | 387 | { | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 388 | unsigned long min, max; | 
|  | 389 | if (cpufreq_get_hardware_limits(cpu, &min, &max)) | 
|  | 390 | return -EINVAL; | 
|  | 391 | printf("%lu %lu\n", min, max); | 
|  | 392 | return 0; | 
|  | 393 | } | 
|  | 394 |  | 
|  | 395 | /* --driver / -d */ | 
|  | 396 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 397 | static int get_driver(unsigned int cpu) | 
|  | 398 | { | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 399 | char *driver = cpufreq_get_driver(cpu); | 
|  | 400 | if (!driver) | 
|  | 401 | return -EINVAL; | 
|  | 402 | printf("%s\n", driver); | 
|  | 403 | cpufreq_put_driver(driver); | 
|  | 404 | return 0; | 
|  | 405 | } | 
|  | 406 |  | 
|  | 407 | /* --policy / -p */ | 
|  | 408 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 409 | static int get_policy(unsigned int cpu) | 
|  | 410 | { | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 411 | struct cpufreq_policy *policy = cpufreq_get_policy(cpu); | 
|  | 412 | if (!policy) | 
|  | 413 | return -EINVAL; | 
|  | 414 | printf("%lu %lu %s\n", policy->min, policy->max, policy->governor); | 
|  | 415 | cpufreq_put_policy(policy); | 
|  | 416 | return 0; | 
|  | 417 | } | 
|  | 418 |  | 
|  | 419 | /* --governors / -g */ | 
|  | 420 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 421 | static int get_available_governors(unsigned int cpu) | 
|  | 422 | { | 
|  | 423 | struct cpufreq_available_governors *governors = | 
|  | 424 | cpufreq_get_available_governors(cpu); | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 425 | if (!governors) | 
|  | 426 | return -EINVAL; | 
|  | 427 |  | 
|  | 428 | while (governors->next) { | 
|  | 429 | printf("%s ", governors->governor); | 
|  | 430 | governors = governors->next; | 
|  | 431 | } | 
|  | 432 | printf("%s\n", governors->governor); | 
|  | 433 | cpufreq_put_available_governors(governors); | 
|  | 434 | return 0; | 
|  | 435 | } | 
|  | 436 |  | 
|  | 437 |  | 
|  | 438 | /* --affected-cpus  / -a */ | 
|  | 439 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 440 | static int get_affected_cpus(unsigned int cpu) | 
|  | 441 | { | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 442 | struct cpufreq_affected_cpus *cpus = cpufreq_get_affected_cpus(cpu); | 
|  | 443 | if (!cpus) | 
|  | 444 | return -EINVAL; | 
|  | 445 |  | 
|  | 446 | while (cpus->next) { | 
|  | 447 | printf("%d ", cpus->cpu); | 
|  | 448 | cpus = cpus->next; | 
|  | 449 | } | 
|  | 450 | printf("%d\n", cpus->cpu); | 
|  | 451 | cpufreq_put_affected_cpus(cpus); | 
|  | 452 | return 0; | 
|  | 453 | } | 
|  | 454 |  | 
|  | 455 | /* --related-cpus  / -r */ | 
|  | 456 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 457 | static int get_related_cpus(unsigned int cpu) | 
|  | 458 | { | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 459 | struct cpufreq_affected_cpus *cpus = cpufreq_get_related_cpus(cpu); | 
|  | 460 | if (!cpus) | 
|  | 461 | return -EINVAL; | 
|  | 462 |  | 
|  | 463 | while (cpus->next) { | 
|  | 464 | printf("%d ", cpus->cpu); | 
|  | 465 | cpus = cpus->next; | 
|  | 466 | } | 
|  | 467 | printf("%d\n", cpus->cpu); | 
|  | 468 | cpufreq_put_related_cpus(cpus); | 
|  | 469 | return 0; | 
|  | 470 | } | 
|  | 471 |  | 
|  | 472 | /* --stats / -s */ | 
|  | 473 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 474 | static int get_freq_stats(unsigned int cpu, unsigned int human) | 
|  | 475 | { | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 476 | unsigned long total_trans = cpufreq_get_transitions(cpu); | 
|  | 477 | unsigned long long total_time; | 
|  | 478 | struct cpufreq_stats *stats = cpufreq_get_stats(cpu, &total_time); | 
|  | 479 | while (stats) { | 
|  | 480 | if (human) { | 
|  | 481 | print_speed(stats->frequency); | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 482 | printf(":%.2f%%", | 
|  | 483 | (100.0 * stats->time_in_state) / total_time); | 
|  | 484 | } else | 
|  | 485 | printf("%lu:%llu", | 
|  | 486 | stats->frequency, stats->time_in_state); | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 487 | stats = stats->next; | 
|  | 488 | if (stats) | 
|  | 489 | printf(", "); | 
|  | 490 | } | 
|  | 491 | cpufreq_put_stats(stats); | 
|  | 492 | if (total_trans) | 
|  | 493 | printf("  (%lu)\n", total_trans); | 
|  | 494 | return 0; | 
|  | 495 | } | 
|  | 496 |  | 
|  | 497 | /* --latency / -y */ | 
|  | 498 |  | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 499 | static int get_latency(unsigned int cpu, unsigned int human) | 
|  | 500 | { | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 501 | unsigned long latency = cpufreq_get_transition_latency(cpu); | 
|  | 502 | if (!latency) | 
|  | 503 | return -EINVAL; | 
|  | 504 |  | 
|  | 505 | if (human) { | 
|  | 506 | print_duration(latency); | 
|  | 507 | printf("\n"); | 
|  | 508 | } else | 
|  | 509 | printf("%lu\n", latency); | 
|  | 510 | return 0; | 
|  | 511 | } | 
|  | 512 |  | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 513 | static struct option info_opts[] = { | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 514 | { .name = "debug",	.has_arg = no_argument,		.flag = NULL,	.val = 'e'}, | 
|  | 515 | { .name = "boost",	.has_arg = no_argument,		.flag = NULL,	.val = 'b'}, | 
|  | 516 | { .name = "freq",	.has_arg = no_argument,		.flag = NULL,	.val = 'f'}, | 
|  | 517 | { .name = "hwfreq",	.has_arg = no_argument,		.flag = NULL,	.val = 'w'}, | 
|  | 518 | { .name = "hwlimits",	.has_arg = no_argument,		.flag = NULL,	.val = 'l'}, | 
|  | 519 | { .name = "driver",	.has_arg = no_argument,		.flag = NULL,	.val = 'd'}, | 
|  | 520 | { .name = "policy",	.has_arg = no_argument,		.flag = NULL,	.val = 'p'}, | 
|  | 521 | { .name = "governors",	.has_arg = no_argument,		.flag = NULL,	.val = 'g'}, | 
|  | 522 | { .name = "related-cpus", .has_arg = no_argument,	.flag = NULL,	.val = 'r'}, | 
|  | 523 | { .name = "affected-cpus",.has_arg = no_argument,	.flag = NULL,	.val = 'a'}, | 
|  | 524 | { .name = "stats",	.has_arg = no_argument,		.flag = NULL,	.val = 's'}, | 
|  | 525 | { .name = "latency",	.has_arg = no_argument,		.flag = NULL,	.val = 'y'}, | 
|  | 526 | { .name = "proc",	.has_arg = no_argument,		.flag = NULL,	.val = 'o'}, | 
|  | 527 | { .name = "human",	.has_arg = no_argument,		.flag = NULL,	.val = 'm'}, | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 528 | { }, | 
|  | 529 | }; | 
|  | 530 |  | 
|  | 531 | int cmd_freq_info(int argc, char **argv) | 
|  | 532 | { | 
|  | 533 | extern char *optarg; | 
|  | 534 | extern int optind, opterr, optopt; | 
|  | 535 | int ret = 0, cont = 1; | 
|  | 536 | unsigned int cpu = 0; | 
|  | 537 | unsigned int human = 0; | 
|  | 538 | int output_param = 0; | 
|  | 539 |  | 
|  | 540 | do { | 
| Dominik Brodowski | 498ca79 | 2011-08-06 18:11:43 +0200 | [diff] [blame] | 541 | ret = getopt_long(argc, argv, "oefwldpgrasmyb", info_opts, NULL); | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 542 | switch (ret) { | 
|  | 543 | case '?': | 
|  | 544 | output_param = '?'; | 
|  | 545 | cont = 0; | 
|  | 546 | break; | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 547 | case -1: | 
|  | 548 | cont = 0; | 
|  | 549 | break; | 
|  | 550 | case 'b': | 
|  | 551 | case 'o': | 
|  | 552 | case 'a': | 
|  | 553 | case 'r': | 
|  | 554 | case 'g': | 
|  | 555 | case 'p': | 
|  | 556 | case 'd': | 
|  | 557 | case 'l': | 
|  | 558 | case 'w': | 
|  | 559 | case 'f': | 
|  | 560 | case 'e': | 
|  | 561 | case 's': | 
|  | 562 | case 'y': | 
|  | 563 | if (output_param) { | 
|  | 564 | output_param = -1; | 
|  | 565 | cont = 0; | 
|  | 566 | break; | 
|  | 567 | } | 
|  | 568 | output_param = ret; | 
|  | 569 | break; | 
|  | 570 | case 'm': | 
|  | 571 | if (human) { | 
|  | 572 | output_param = -1; | 
|  | 573 | cont = 0; | 
|  | 574 | break; | 
|  | 575 | } | 
|  | 576 | human = 1; | 
|  | 577 | break; | 
|  | 578 | default: | 
|  | 579 | fprintf(stderr, "invalid or unknown argument\n"); | 
|  | 580 | return EXIT_FAILURE; | 
|  | 581 | } | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 582 | } while (cont); | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 583 |  | 
|  | 584 | switch (output_param) { | 
|  | 585 | case 'o': | 
|  | 586 | if (!bitmask_isallclear(cpus_chosen)) { | 
|  | 587 | printf(_("The argument passed to this tool can't be " | 
|  | 588 | "combined with passing a --cpu argument\n")); | 
|  | 589 | return -EINVAL; | 
|  | 590 | } | 
|  | 591 | break; | 
|  | 592 | case 0: | 
|  | 593 | output_param = 'e'; | 
|  | 594 | } | 
|  | 595 |  | 
|  | 596 | ret = 0; | 
|  | 597 |  | 
|  | 598 | /* Default is: show output of CPU 0 only */ | 
|  | 599 | if (bitmask_isallclear(cpus_chosen)) | 
|  | 600 | bitmask_setbit(cpus_chosen, 0); | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 601 |  | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 602 | switch (output_param) { | 
|  | 603 | case -1: | 
|  | 604 | printf(_("You can't specify more than one --cpu parameter and/or\n" | 
|  | 605 | "more than one output-specific argument\n")); | 
|  | 606 | return -EINVAL; | 
|  | 607 | case '?': | 
|  | 608 | printf(_("invalid or unknown argument\n")); | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 609 | return -EINVAL; | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 610 | case 'o': | 
|  | 611 | proc_cpufreq_output(); | 
|  | 612 | return EXIT_SUCCESS; | 
|  | 613 | } | 
|  | 614 |  | 
|  | 615 | for (cpu = bitmask_first(cpus_chosen); | 
|  | 616 | cpu <= bitmask_last(cpus_chosen); cpu++) { | 
|  | 617 |  | 
|  | 618 | if (!bitmask_isbitset(cpus_chosen, cpu)) | 
|  | 619 | continue; | 
|  | 620 | if (cpufreq_cpu_exists(cpu)) { | 
|  | 621 | printf(_("couldn't analyze CPU %d as it doesn't seem to be present\n"), cpu); | 
|  | 622 | continue; | 
|  | 623 | } | 
|  | 624 | printf(_("analyzing CPU %d:\n"), cpu); | 
|  | 625 |  | 
|  | 626 | switch (output_param) { | 
|  | 627 | case 'b': | 
|  | 628 | get_boost_mode(cpu); | 
|  | 629 | break; | 
|  | 630 | case 'e': | 
|  | 631 | debug_output_one(cpu); | 
|  | 632 | break; | 
|  | 633 | case 'a': | 
|  | 634 | ret = get_affected_cpus(cpu); | 
|  | 635 | break; | 
|  | 636 | case 'r': | 
|  | 637 | ret = get_related_cpus(cpu); | 
|  | 638 | break; | 
|  | 639 | case 'g': | 
|  | 640 | ret = get_available_governors(cpu); | 
|  | 641 | break; | 
|  | 642 | case 'p': | 
|  | 643 | ret = get_policy(cpu); | 
|  | 644 | break; | 
|  | 645 | case 'd': | 
|  | 646 | ret = get_driver(cpu); | 
|  | 647 | break; | 
|  | 648 | case 'l': | 
|  | 649 | ret = get_hardware_limits(cpu); | 
|  | 650 | break; | 
|  | 651 | case 'w': | 
|  | 652 | ret = get_freq_hardware(cpu, human); | 
|  | 653 | break; | 
|  | 654 | case 'f': | 
|  | 655 | ret = get_freq_kernel(cpu, human); | 
|  | 656 | break; | 
|  | 657 | case 's': | 
|  | 658 | ret = get_freq_stats(cpu, human); | 
|  | 659 | break; | 
|  | 660 | case 'y': | 
|  | 661 | ret = get_latency(cpu, human); | 
|  | 662 | break; | 
|  | 663 | } | 
|  | 664 | if (ret) | 
| Dominik Brodowski | a1ce5ba | 2011-04-19 20:33:50 +0200 | [diff] [blame] | 665 | return ret; | 
| Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 666 | } | 
|  | 667 | return ret; | 
|  | 668 | } |