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