Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * drivers/cpufreq/cpufreq_stats.c |
| 3 | * |
| 4 | * Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>. |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 5 | * (C) 2004 Zou Nan hai <nanhai.zou@intel.com>. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License version 2 as |
| 9 | * published by the Free Software Foundation. |
| 10 | */ |
| 11 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 12 | #include <linux/cpu.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 13 | #include <linux/cpufreq.h> |
Paul Gortmaker | 5c720d37 | 2011-05-27 13:23:32 -0400 | [diff] [blame] | 14 | #include <linux/module.h> |
Viresh Kumar | 5ff0a26 | 2013-08-06 22:53:03 +0530 | [diff] [blame] | 15 | #include <linux/slab.h> |
Frederic Weisbecker | bfc3f02 | 2014-03-05 16:33:42 +0100 | [diff] [blame] | 16 | #include <linux/cputime.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 17 | |
| 18 | static spinlock_t cpufreq_stats_lock; |
| 19 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 20 | struct cpufreq_stats { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 21 | unsigned int total_trans; |
Viresh Kumar | bb176f7 | 2013-06-19 14:19:33 +0530 | [diff] [blame] | 22 | unsigned long long last_time; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 23 | unsigned int max_state; |
| 24 | unsigned int state_num; |
| 25 | unsigned int last_index; |
Viresh Kumar | 1e7586a | 2012-10-26 00:51:21 +0200 | [diff] [blame] | 26 | u64 *time_in_state; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 27 | unsigned int *freq_table; |
| 28 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS |
| 29 | unsigned int *trans_table; |
| 30 | #endif |
| 31 | }; |
| 32 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 33 | static int cpufreq_stats_update(struct cpufreq_stats *stats) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 34 | { |
Viresh Kumar | 9531347 | 2015-01-06 21:09:03 +0530 | [diff] [blame] | 35 | unsigned long long cur_time = get_jiffies_64(); |
Venkatesh Pallipadi | 58f1df2 | 2005-05-25 14:46:50 -0700 | [diff] [blame] | 36 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 37 | spin_lock(&cpufreq_stats_lock); |
Viresh Kumar | c960f9b | 2015-01-06 21:09:12 +0530 | [diff] [blame^] | 38 | stats->time_in_state[stats->last_index] += cur_time - stats->last_time; |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 39 | stats->last_time = cur_time; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | spin_unlock(&cpufreq_stats_lock); |
| 41 | return 0; |
| 42 | } |
| 43 | |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 44 | static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 | { |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 46 | return sprintf(buf, "%d\n", policy->stats->total_trans); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 47 | } |
| 48 | |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 49 | static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 50 | { |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 51 | struct cpufreq_stats *stats = policy->stats; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 52 | ssize_t len = 0; |
| 53 | int i; |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 54 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 55 | cpufreq_stats_update(stats); |
| 56 | for (i = 0; i < stats->state_num; i++) { |
| 57 | len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i], |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 58 | (unsigned long long) |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 59 | jiffies_64_to_clock_t(stats->time_in_state[i])); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 60 | } |
| 61 | return len; |
| 62 | } |
| 63 | |
| 64 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 65 | static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 66 | { |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 67 | struct cpufreq_stats *stats = policy->stats; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 68 | ssize_t len = 0; |
| 69 | int i, j; |
| 70 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 71 | cpufreq_stats_update(stats); |
Venkatesh Pallipadi | 58f1df2 | 2005-05-25 14:46:50 -0700 | [diff] [blame] | 72 | len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); |
| 73 | len += snprintf(buf + len, PAGE_SIZE - len, " : "); |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 74 | for (i = 0; i < stats->state_num; i++) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 75 | if (len >= PAGE_SIZE) |
| 76 | break; |
Venkatesh Pallipadi | 58f1df2 | 2005-05-25 14:46:50 -0700 | [diff] [blame] | 77 | len += snprintf(buf + len, PAGE_SIZE - len, "%9u ", |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 78 | stats->freq_table[i]); |
Venkatesh Pallipadi | 58f1df2 | 2005-05-25 14:46:50 -0700 | [diff] [blame] | 79 | } |
| 80 | if (len >= PAGE_SIZE) |
Cesar Eduardo Barros | 25aca34 | 2008-02-16 08:41:25 -0200 | [diff] [blame] | 81 | return PAGE_SIZE; |
Venkatesh Pallipadi | 58f1df2 | 2005-05-25 14:46:50 -0700 | [diff] [blame] | 82 | |
| 83 | len += snprintf(buf + len, PAGE_SIZE - len, "\n"); |
| 84 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 85 | for (i = 0; i < stats->state_num; i++) { |
Venkatesh Pallipadi | 58f1df2 | 2005-05-25 14:46:50 -0700 | [diff] [blame] | 86 | if (len >= PAGE_SIZE) |
| 87 | break; |
| 88 | |
| 89 | len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ", |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 90 | stats->freq_table[i]); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 91 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 92 | for (j = 0; j < stats->state_num; j++) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 93 | if (len >= PAGE_SIZE) |
| 94 | break; |
Venkatesh Pallipadi | 58f1df2 | 2005-05-25 14:46:50 -0700 | [diff] [blame] | 95 | len += snprintf(buf + len, PAGE_SIZE - len, "%9u ", |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 96 | stats->trans_table[i*stats->max_state+j]); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 97 | } |
Cesar Eduardo Barros | 25aca34 | 2008-02-16 08:41:25 -0200 | [diff] [blame] | 98 | if (len >= PAGE_SIZE) |
| 99 | break; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 100 | len += snprintf(buf + len, PAGE_SIZE - len, "\n"); |
| 101 | } |
Cesar Eduardo Barros | 25aca34 | 2008-02-16 08:41:25 -0200 | [diff] [blame] | 102 | if (len >= PAGE_SIZE) |
| 103 | return PAGE_SIZE; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 104 | return len; |
| 105 | } |
Viresh Kumar | df18e50 | 2013-02-04 11:38:52 +0000 | [diff] [blame] | 106 | cpufreq_freq_attr_ro(trans_table); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 107 | #endif |
| 108 | |
Viresh Kumar | df18e50 | 2013-02-04 11:38:52 +0000 | [diff] [blame] | 109 | cpufreq_freq_attr_ro(total_trans); |
| 110 | cpufreq_freq_attr_ro(time_in_state); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 111 | |
| 112 | static struct attribute *default_attrs[] = { |
Viresh Kumar | df18e50 | 2013-02-04 11:38:52 +0000 | [diff] [blame] | 113 | &total_trans.attr, |
| 114 | &time_in_state.attr, |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 115 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS |
Viresh Kumar | df18e50 | 2013-02-04 11:38:52 +0000 | [diff] [blame] | 116 | &trans_table.attr, |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 117 | #endif |
| 118 | NULL |
| 119 | }; |
| 120 | static struct attribute_group stats_attr_group = { |
| 121 | .attrs = default_attrs, |
| 122 | .name = "stats" |
| 123 | }; |
| 124 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 125 | static int freq_table_get_index(struct cpufreq_stats *stats, unsigned int freq) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 126 | { |
| 127 | int index; |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 128 | for (index = 0; index < stats->max_state; index++) |
| 129 | if (stats->freq_table[index] == freq) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 130 | return index; |
| 131 | return -1; |
| 132 | } |
| 133 | |
Viresh Kumar | 2d13594 | 2014-01-07 07:10:12 +0530 | [diff] [blame] | 134 | static void __cpufreq_stats_free_table(struct cpufreq_policy *policy) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 135 | { |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 136 | struct cpufreq_stats *stats = policy->stats; |
Viresh Kumar | b8eed8a | 2013-01-14 13:23:03 +0000 | [diff] [blame] | 137 | |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 138 | /* Already freed */ |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 139 | if (!stats) |
Viresh Kumar | 2d13594 | 2014-01-07 07:10:12 +0530 | [diff] [blame] | 140 | return; |
| 141 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 142 | pr_debug("%s: Free stats table\n", __func__); |
Viresh Kumar | 2d13594 | 2014-01-07 07:10:12 +0530 | [diff] [blame] | 143 | |
| 144 | sysfs_remove_group(&policy->kobj, &stats_attr_group); |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 145 | kfree(stats->time_in_state); |
| 146 | kfree(stats); |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 147 | policy->stats = NULL; |
steven finney | 98586ed | 2011-05-02 11:29:17 -0700 | [diff] [blame] | 148 | } |
| 149 | |
Viresh Kumar | 2d13594 | 2014-01-07 07:10:12 +0530 | [diff] [blame] | 150 | static void cpufreq_stats_free_table(unsigned int cpu) |
steven finney | 98586ed | 2011-05-02 11:29:17 -0700 | [diff] [blame] | 151 | { |
Viresh Kumar | 2d13594 | 2014-01-07 07:10:12 +0530 | [diff] [blame] | 152 | struct cpufreq_policy *policy; |
Dirk Brandewie | 633d47d | 2013-02-06 09:02:12 -0800 | [diff] [blame] | 153 | |
Viresh Kumar | 2d13594 | 2014-01-07 07:10:12 +0530 | [diff] [blame] | 154 | policy = cpufreq_cpu_get(cpu); |
viresh kumar | 187da1d | 2013-03-22 10:13:52 +0000 | [diff] [blame] | 155 | if (!policy) |
Dirk Brandewie | 633d47d | 2013-02-06 09:02:12 -0800 | [diff] [blame] | 156 | return; |
| 157 | |
Viresh Kumar | f93dbbb | 2015-01-06 21:09:04 +0530 | [diff] [blame] | 158 | __cpufreq_stats_free_table(policy); |
viresh kumar | 187da1d | 2013-03-22 10:13:52 +0000 | [diff] [blame] | 159 | |
viresh kumar | 187da1d | 2013-03-22 10:13:52 +0000 | [diff] [blame] | 160 | cpufreq_cpu_put(policy); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 161 | } |
| 162 | |
Saravana Kannan | ad4c230 | 2014-02-27 17:58:36 -0800 | [diff] [blame] | 163 | static int __cpufreq_stats_create_table(struct cpufreq_policy *policy) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 164 | { |
Viresh Kumar | a685c6d | 2015-01-06 21:09:11 +0530 | [diff] [blame] | 165 | unsigned int i = 0, count = 0, ret = -ENOMEM; |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 166 | struct cpufreq_stats *stats; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 167 | unsigned int alloc_size; |
| 168 | unsigned int cpu = policy->cpu; |
Stratos Karafotis | 041526f | 2014-04-25 23:15:38 +0300 | [diff] [blame] | 169 | struct cpufreq_frequency_table *pos, *table; |
Saravana Kannan | ad4c230 | 2014-02-27 17:58:36 -0800 | [diff] [blame] | 170 | |
Viresh Kumar | a685c6d | 2015-01-06 21:09:11 +0530 | [diff] [blame] | 171 | /* We need cpufreq table for creating stats table */ |
Saravana Kannan | ad4c230 | 2014-02-27 17:58:36 -0800 | [diff] [blame] | 172 | table = cpufreq_frequency_get_table(cpu); |
| 173 | if (unlikely(!table)) |
| 174 | return 0; |
| 175 | |
Viresh Kumar | b8c6744 | 2015-01-06 21:09:01 +0530 | [diff] [blame] | 176 | /* stats already initialized */ |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 177 | if (policy->stats) |
Viresh Kumar | b8c6744 | 2015-01-06 21:09:01 +0530 | [diff] [blame] | 178 | return -EEXIST; |
| 179 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 180 | stats = kzalloc(sizeof(*stats), GFP_KERNEL); |
Viresh Kumar | a685c6d | 2015-01-06 21:09:11 +0530 | [diff] [blame] | 181 | if (!stats) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 182 | return -ENOMEM; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 183 | |
Viresh Kumar | a685c6d | 2015-01-06 21:09:11 +0530 | [diff] [blame] | 184 | /* Find total allocation size */ |
Stratos Karafotis | 041526f | 2014-04-25 23:15:38 +0300 | [diff] [blame] | 185 | cpufreq_for_each_valid_entry(pos, table) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 186 | count++; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 187 | |
Viresh Kumar | 1e7586a | 2012-10-26 00:51:21 +0200 | [diff] [blame] | 188 | alloc_size = count * sizeof(int) + count * sizeof(u64); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 189 | |
| 190 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS |
| 191 | alloc_size += count * count * sizeof(int); |
| 192 | #endif |
Viresh Kumar | a685c6d | 2015-01-06 21:09:11 +0530 | [diff] [blame] | 193 | |
| 194 | /* Allocate memory for time_in_state/freq_table/trans_table in one go */ |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 195 | stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL); |
Viresh Kumar | a685c6d | 2015-01-06 21:09:11 +0530 | [diff] [blame] | 196 | if (!stats->time_in_state) |
| 197 | goto free_stat; |
| 198 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 199 | stats->freq_table = (unsigned int *)(stats->time_in_state + count); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 200 | |
| 201 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 202 | stats->trans_table = stats->freq_table + count; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 203 | #endif |
Viresh Kumar | a685c6d | 2015-01-06 21:09:11 +0530 | [diff] [blame] | 204 | |
| 205 | stats->max_state = count; |
| 206 | |
| 207 | /* Find valid-unique entries */ |
Stratos Karafotis | 041526f | 2014-04-25 23:15:38 +0300 | [diff] [blame] | 208 | cpufreq_for_each_valid_entry(pos, table) |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 209 | if (freq_table_get_index(stats, pos->frequency) == -1) |
| 210 | stats->freq_table[i++] = pos->frequency; |
| 211 | stats->state_num = i; |
Viresh Kumar | a685c6d | 2015-01-06 21:09:11 +0530 | [diff] [blame] | 212 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 213 | spin_lock(&cpufreq_stats_lock); |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 214 | stats->last_time = get_jiffies_64(); |
| 215 | stats->last_index = freq_table_get_index(stats, policy->cur); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 216 | spin_unlock(&cpufreq_stats_lock); |
Viresh Kumar | a685c6d | 2015-01-06 21:09:11 +0530 | [diff] [blame] | 217 | |
| 218 | policy->stats = stats; |
| 219 | ret = sysfs_create_group(&policy->kobj, &stats_attr_group); |
| 220 | if (!ret) |
| 221 | return 0; |
| 222 | |
| 223 | /* We failed, release resources */ |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 224 | policy->stats = NULL; |
Viresh Kumar | a685c6d | 2015-01-06 21:09:11 +0530 | [diff] [blame] | 225 | kfree(stats->time_in_state); |
| 226 | free_stat: |
| 227 | kfree(stats); |
| 228 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 229 | return ret; |
| 230 | } |
| 231 | |
Viresh Kumar | b3f9ff8 | 2014-01-07 07:10:13 +0530 | [diff] [blame] | 232 | static void cpufreq_stats_create_table(unsigned int cpu) |
| 233 | { |
| 234 | struct cpufreq_policy *policy; |
Viresh Kumar | b3f9ff8 | 2014-01-07 07:10:13 +0530 | [diff] [blame] | 235 | |
| 236 | /* |
| 237 | * "likely(!policy)" because normally cpufreq_stats will be registered |
| 238 | * before cpufreq driver |
| 239 | */ |
| 240 | policy = cpufreq_cpu_get(cpu); |
| 241 | if (likely(!policy)) |
| 242 | return; |
| 243 | |
Saravana Kannan | ad4c230 | 2014-02-27 17:58:36 -0800 | [diff] [blame] | 244 | __cpufreq_stats_create_table(policy); |
Viresh Kumar | b3f9ff8 | 2014-01-07 07:10:13 +0530 | [diff] [blame] | 245 | |
| 246 | cpufreq_cpu_put(policy); |
| 247 | } |
| 248 | |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 249 | static int cpufreq_stat_notifier_policy(struct notifier_block *nb, |
| 250 | unsigned long val, void *data) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 251 | { |
Viresh Kumar | fcd7af9 | 2014-01-07 07:10:10 +0530 | [diff] [blame] | 252 | int ret = 0; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 253 | struct cpufreq_policy *policy = data; |
Viresh Kumar | b8eed8a | 2013-01-14 13:23:03 +0000 | [diff] [blame] | 254 | |
Viresh Kumar | fcd7af9 | 2014-01-07 07:10:10 +0530 | [diff] [blame] | 255 | if (val == CPUFREQ_CREATE_POLICY) |
Saravana Kannan | ad4c230 | 2014-02-27 17:58:36 -0800 | [diff] [blame] | 256 | ret = __cpufreq_stats_create_table(policy); |
Viresh Kumar | 2d13594 | 2014-01-07 07:10:12 +0530 | [diff] [blame] | 257 | else if (val == CPUFREQ_REMOVE_POLICY) |
| 258 | __cpufreq_stats_free_table(policy); |
Viresh Kumar | fcd7af9 | 2014-01-07 07:10:10 +0530 | [diff] [blame] | 259 | |
| 260 | return ret; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 261 | } |
| 262 | |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 263 | static int cpufreq_stat_notifier_trans(struct notifier_block *nb, |
| 264 | unsigned long val, void *data) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 265 | { |
| 266 | struct cpufreq_freqs *freq = data; |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 267 | struct cpufreq_policy *policy = cpufreq_cpu_get(freq->cpu); |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 268 | struct cpufreq_stats *stats; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 269 | int old_index, new_index; |
| 270 | |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 271 | if (!policy) { |
| 272 | pr_err("%s: No policy found\n", __func__); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 273 | return 0; |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 274 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 275 | |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 276 | if (val != CPUFREQ_POSTCHANGE) |
| 277 | goto put_policy; |
| 278 | |
| 279 | if (!policy->stats) { |
| 280 | pr_debug("%s: No stats found\n", __func__); |
| 281 | goto put_policy; |
| 282 | } |
| 283 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 284 | stats = policy->stats; |
Venkatesh Pallipadi | 8edc59d9 | 2006-12-19 12:58:55 -0800 | [diff] [blame] | 285 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 286 | old_index = stats->last_index; |
| 287 | new_index = freq_table_get_index(stats, freq->new); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 288 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 289 | /* We can't do stats->time_in_state[-1]= .. */ |
Konrad Rzeszutek Wilk | 46a310b | 2011-06-16 15:36:38 -0400 | [diff] [blame] | 290 | if (old_index == -1 || new_index == -1) |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 291 | goto put_policy; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 292 | |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 293 | cpufreq_stats_update(stats); |
Konrad Rzeszutek Wilk | 46a310b | 2011-06-16 15:36:38 -0400 | [diff] [blame] | 294 | |
| 295 | if (old_index == new_index) |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 296 | goto put_policy; |
Venkatesh Pallipadi | 8edc59d9 | 2006-12-19 12:58:55 -0800 | [diff] [blame] | 297 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 298 | spin_lock(&cpufreq_stats_lock); |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 299 | stats->last_index = new_index; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 300 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 301 | stats->trans_table[old_index * stats->max_state + new_index]++; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 302 | #endif |
Viresh Kumar | 5094160 | 2015-01-06 21:09:07 +0530 | [diff] [blame] | 303 | stats->total_trans++; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 304 | spin_unlock(&cpufreq_stats_lock); |
Viresh Kumar | a9aaf29 | 2015-01-13 11:34:00 +0530 | [diff] [blame] | 305 | |
| 306 | put_policy: |
| 307 | cpufreq_cpu_put(policy); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 308 | return 0; |
| 309 | } |
| 310 | |
| 311 | static struct notifier_block notifier_policy_block = { |
| 312 | .notifier_call = cpufreq_stat_notifier_policy |
| 313 | }; |
| 314 | |
| 315 | static struct notifier_block notifier_trans_block = { |
| 316 | .notifier_call = cpufreq_stat_notifier_trans |
| 317 | }; |
| 318 | |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 319 | static int __init cpufreq_stats_init(void) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 320 | { |
| 321 | int ret; |
| 322 | unsigned int cpu; |
Ashok Raj | c32b6b8 | 2005-10-30 14:59:54 -0800 | [diff] [blame] | 323 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 324 | spin_lock_init(&cpufreq_stats_lock); |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 325 | ret = cpufreq_register_notifier(¬ifier_policy_block, |
| 326 | CPUFREQ_POLICY_NOTIFIER); |
| 327 | if (ret) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 328 | return ret; |
| 329 | |
Viresh Kumar | b3f9ff8 | 2014-01-07 07:10:13 +0530 | [diff] [blame] | 330 | for_each_online_cpu(cpu) |
| 331 | cpufreq_stats_create_table(cpu); |
| 332 | |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 333 | ret = cpufreq_register_notifier(¬ifier_trans_block, |
| 334 | CPUFREQ_TRANSITION_NOTIFIER); |
| 335 | if (ret) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 336 | cpufreq_unregister_notifier(¬ifier_policy_block, |
| 337 | CPUFREQ_POLICY_NOTIFIER); |
Konstantin Khlebnikov | 56836fb | 2012-12-15 00:22:02 +0100 | [diff] [blame] | 338 | for_each_online_cpu(cpu) |
| 339 | cpufreq_stats_free_table(cpu); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 340 | return ret; |
| 341 | } |
| 342 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 343 | return 0; |
| 344 | } |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 345 | static void __exit cpufreq_stats_exit(void) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 346 | { |
| 347 | unsigned int cpu; |
Ashok Raj | c32b6b8 | 2005-10-30 14:59:54 -0800 | [diff] [blame] | 348 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 349 | cpufreq_unregister_notifier(¬ifier_policy_block, |
| 350 | CPUFREQ_POLICY_NOTIFIER); |
| 351 | cpufreq_unregister_notifier(¬ifier_trans_block, |
| 352 | CPUFREQ_TRANSITION_NOTIFIER); |
Viresh Kumar | 2d13594 | 2014-01-07 07:10:12 +0530 | [diff] [blame] | 353 | for_each_online_cpu(cpu) |
Satyam Sharma | 55395ae | 2007-10-02 13:28:15 -0700 | [diff] [blame] | 354 | cpufreq_stats_free_table(cpu); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 355 | } |
| 356 | |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 357 | MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>"); |
Viresh Kumar | 00d0b29 | 2015-01-06 21:09:00 +0530 | [diff] [blame] | 358 | MODULE_DESCRIPTION("Export cpufreq stats via sysfs"); |
Dave Jones | 0a829c5 | 2009-01-18 01:49:04 -0500 | [diff] [blame] | 359 | MODULE_LICENSE("GPL"); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 360 | |
| 361 | module_init(cpufreq_stats_init); |
| 362 | module_exit(cpufreq_stats_exit); |