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