11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  drivers/cpufreq/cpufreq_stats.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
51da177e4SLinus Torvalds  *	      (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify
81da177e4SLinus Torvalds  * it under the terms of the GNU General Public License version 2 as
91da177e4SLinus Torvalds  * published by the Free Software Foundation.
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/kernel.h>
131da177e4SLinus Torvalds #include <linux/sysdev.h>
141da177e4SLinus Torvalds #include <linux/cpu.h>
151da177e4SLinus Torvalds #include <linux/sysfs.h>
161da177e4SLinus Torvalds #include <linux/cpufreq.h>
171da177e4SLinus Torvalds #include <linux/jiffies.h>
181da177e4SLinus Torvalds #include <linux/percpu.h>
191da177e4SLinus Torvalds #include <linux/kobject.h>
201da177e4SLinus Torvalds #include <linux/spinlock.h>
21c32b6b8eSAshok Raj #include <linux/notifier.h>
2258f1df25SVenkatesh Pallipadi #include <asm/cputime.h>
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds static spinlock_t cpufreq_stats_lock;
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #define CPUFREQ_STATDEVICE_ATTR(_name,_mode,_show) \
271da177e4SLinus Torvalds static struct freq_attr _attr_##_name = {\
287b595756STejun Heo 	.attr = {.name = __stringify(_name), .mode = _mode, }, \
291da177e4SLinus Torvalds 	.show = _show,\
301da177e4SLinus Torvalds };
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds struct cpufreq_stats {
331da177e4SLinus Torvalds 	unsigned int cpu;
341da177e4SLinus Torvalds 	unsigned int total_trans;
351da177e4SLinus Torvalds 	unsigned long long  last_time;
361da177e4SLinus Torvalds 	unsigned int max_state;
371da177e4SLinus Torvalds 	unsigned int state_num;
381da177e4SLinus Torvalds 	unsigned int last_index;
3958f1df25SVenkatesh Pallipadi 	cputime64_t *time_in_state;
401da177e4SLinus Torvalds 	unsigned int *freq_table;
411da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
421da177e4SLinus Torvalds 	unsigned int *trans_table;
431da177e4SLinus Torvalds #endif
441da177e4SLinus Torvalds };
451da177e4SLinus Torvalds 
467a6aedfaSMike Travis static DEFINE_PER_CPU(struct cpufreq_stats *, cpufreq_stats_table);
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds struct cpufreq_stats_attribute {
491da177e4SLinus Torvalds 	struct attribute attr;
501da177e4SLinus Torvalds 	ssize_t(*show) (struct cpufreq_stats *, char *);
511da177e4SLinus Torvalds };
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds static int
541da177e4SLinus Torvalds cpufreq_stats_update (unsigned int cpu)
551da177e4SLinus Torvalds {
561da177e4SLinus Torvalds 	struct cpufreq_stats *stat;
5758f1df25SVenkatesh Pallipadi 	unsigned long long cur_time;
5858f1df25SVenkatesh Pallipadi 
5958f1df25SVenkatesh Pallipadi 	cur_time = get_jiffies_64();
601da177e4SLinus Torvalds 	spin_lock(&cpufreq_stats_lock);
617a6aedfaSMike Travis 	stat = per_cpu(cpufreq_stats_table, cpu);
621da177e4SLinus Torvalds 	if (stat->time_in_state)
6358f1df25SVenkatesh Pallipadi 		stat->time_in_state[stat->last_index] =
6458f1df25SVenkatesh Pallipadi 			cputime64_add(stat->time_in_state[stat->last_index],
6558f1df25SVenkatesh Pallipadi 				      cputime_sub(cur_time, stat->last_time));
6658f1df25SVenkatesh Pallipadi 	stat->last_time = cur_time;
671da177e4SLinus Torvalds 	spin_unlock(&cpufreq_stats_lock);
681da177e4SLinus Torvalds 	return 0;
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds static ssize_t
721da177e4SLinus Torvalds show_total_trans(struct cpufreq_policy *policy, char *buf)
731da177e4SLinus Torvalds {
747a6aedfaSMike Travis 	struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
751da177e4SLinus Torvalds 	if (!stat)
761da177e4SLinus Torvalds 		return 0;
771da177e4SLinus Torvalds 	return sprintf(buf, "%d\n",
787a6aedfaSMike Travis 			per_cpu(cpufreq_stats_table, stat->cpu)->total_trans);
791da177e4SLinus Torvalds }
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds static ssize_t
821da177e4SLinus Torvalds show_time_in_state(struct cpufreq_policy *policy, char *buf)
831da177e4SLinus Torvalds {
841da177e4SLinus Torvalds 	ssize_t len = 0;
851da177e4SLinus Torvalds 	int i;
867a6aedfaSMike Travis 	struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
871da177e4SLinus Torvalds 	if (!stat)
881da177e4SLinus Torvalds 		return 0;
891da177e4SLinus Torvalds 	cpufreq_stats_update(stat->cpu);
901da177e4SLinus Torvalds 	for (i = 0; i < stat->state_num; i++) {
9158f1df25SVenkatesh Pallipadi 		len += sprintf(buf + len, "%u %llu\n", stat->freq_table[i],
9258f1df25SVenkatesh Pallipadi 			(unsigned long long)cputime64_to_clock_t(stat->time_in_state[i]));
931da177e4SLinus Torvalds 	}
941da177e4SLinus Torvalds 	return len;
951da177e4SLinus Torvalds }
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
981da177e4SLinus Torvalds static ssize_t
991da177e4SLinus Torvalds show_trans_table(struct cpufreq_policy *policy, char *buf)
1001da177e4SLinus Torvalds {
1011da177e4SLinus Torvalds 	ssize_t len = 0;
1021da177e4SLinus Torvalds 	int i, j;
1031da177e4SLinus Torvalds 
1047a6aedfaSMike Travis 	struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
1051da177e4SLinus Torvalds 	if (!stat)
1061da177e4SLinus Torvalds 		return 0;
1071da177e4SLinus Torvalds 	cpufreq_stats_update(stat->cpu);
10858f1df25SVenkatesh Pallipadi 	len += snprintf(buf + len, PAGE_SIZE - len, "   From  :    To\n");
10958f1df25SVenkatesh Pallipadi 	len += snprintf(buf + len, PAGE_SIZE - len, "         : ");
1101da177e4SLinus Torvalds 	for (i = 0; i < stat->state_num; i++) {
1111da177e4SLinus Torvalds 		if (len >= PAGE_SIZE)
1121da177e4SLinus Torvalds 			break;
11358f1df25SVenkatesh Pallipadi 		len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
11458f1df25SVenkatesh Pallipadi 				stat->freq_table[i]);
11558f1df25SVenkatesh Pallipadi 	}
11658f1df25SVenkatesh Pallipadi 	if (len >= PAGE_SIZE)
11725aca347SCesar Eduardo Barros 		return PAGE_SIZE;
11858f1df25SVenkatesh Pallipadi 
11958f1df25SVenkatesh Pallipadi 	len += snprintf(buf + len, PAGE_SIZE - len, "\n");
12058f1df25SVenkatesh Pallipadi 
12158f1df25SVenkatesh Pallipadi 	for (i = 0; i < stat->state_num; i++) {
12258f1df25SVenkatesh Pallipadi 		if (len >= PAGE_SIZE)
12358f1df25SVenkatesh Pallipadi 			break;
12458f1df25SVenkatesh Pallipadi 
12558f1df25SVenkatesh Pallipadi 		len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ",
1261da177e4SLinus Torvalds 				stat->freq_table[i]);
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 		for (j = 0; j < stat->state_num; j++)   {
1291da177e4SLinus Torvalds 			if (len >= PAGE_SIZE)
1301da177e4SLinus Torvalds 				break;
13158f1df25SVenkatesh Pallipadi 			len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
1321da177e4SLinus Torvalds 					stat->trans_table[i*stat->max_state+j]);
1331da177e4SLinus Torvalds 		}
13425aca347SCesar Eduardo Barros 		if (len >= PAGE_SIZE)
13525aca347SCesar Eduardo Barros 			break;
1361da177e4SLinus Torvalds 		len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1371da177e4SLinus Torvalds 	}
13825aca347SCesar Eduardo Barros 	if (len >= PAGE_SIZE)
13925aca347SCesar Eduardo Barros 		return PAGE_SIZE;
1401da177e4SLinus Torvalds 	return len;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds CPUFREQ_STATDEVICE_ATTR(trans_table,0444,show_trans_table);
1431da177e4SLinus Torvalds #endif
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds CPUFREQ_STATDEVICE_ATTR(total_trans,0444,show_total_trans);
1461da177e4SLinus Torvalds CPUFREQ_STATDEVICE_ATTR(time_in_state,0444,show_time_in_state);
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds static struct attribute *default_attrs[] = {
1491da177e4SLinus Torvalds 	&_attr_total_trans.attr,
1501da177e4SLinus Torvalds 	&_attr_time_in_state.attr,
1511da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
1521da177e4SLinus Torvalds 	&_attr_trans_table.attr,
1531da177e4SLinus Torvalds #endif
1541da177e4SLinus Torvalds 	NULL
1551da177e4SLinus Torvalds };
1561da177e4SLinus Torvalds static struct attribute_group stats_attr_group = {
1571da177e4SLinus Torvalds 	.attrs = default_attrs,
1581da177e4SLinus Torvalds 	.name = "stats"
1591da177e4SLinus Torvalds };
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds static int
1621da177e4SLinus Torvalds freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
1631da177e4SLinus Torvalds {
1641da177e4SLinus Torvalds 	int index;
1651da177e4SLinus Torvalds 	for (index = 0; index < stat->max_state; index++)
1661da177e4SLinus Torvalds 		if (stat->freq_table[index] == freq)
1671da177e4SLinus Torvalds 			return index;
1681da177e4SLinus Torvalds 	return -1;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
171a3323473SAdrian Bunk static void cpufreq_stats_free_table(unsigned int cpu)
1721da177e4SLinus Torvalds {
1737a6aedfaSMike Travis 	struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu);
1741da177e4SLinus Torvalds 	struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
1751da177e4SLinus Torvalds 	if (policy && policy->cpu == cpu)
1761da177e4SLinus Torvalds 		sysfs_remove_group(&policy->kobj, &stats_attr_group);
1771da177e4SLinus Torvalds 	if (stat) {
1781da177e4SLinus Torvalds 		kfree(stat->time_in_state);
1791da177e4SLinus Torvalds 		kfree(stat);
1801da177e4SLinus Torvalds 	}
1817a6aedfaSMike Travis 	per_cpu(cpufreq_stats_table, cpu) = NULL;
1821da177e4SLinus Torvalds 	if (policy)
1831da177e4SLinus Torvalds 		cpufreq_cpu_put(policy);
1841da177e4SLinus Torvalds }
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds static int
1871da177e4SLinus Torvalds cpufreq_stats_create_table (struct cpufreq_policy *policy,
1881da177e4SLinus Torvalds 		struct cpufreq_frequency_table *table)
1891da177e4SLinus Torvalds {
1901da177e4SLinus Torvalds 	unsigned int i, j, count = 0, ret = 0;
1911da177e4SLinus Torvalds 	struct cpufreq_stats *stat;
1921da177e4SLinus Torvalds 	struct cpufreq_policy *data;
1931da177e4SLinus Torvalds 	unsigned int alloc_size;
1941da177e4SLinus Torvalds 	unsigned int cpu = policy->cpu;
1957a6aedfaSMike Travis 	if (per_cpu(cpufreq_stats_table, cpu))
1961da177e4SLinus Torvalds 		return -EBUSY;
197e98df50cSDave Jones 	if ((stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL)) == NULL)
1981da177e4SLinus Torvalds 		return -ENOMEM;
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 	data = cpufreq_cpu_get(cpu);
201bc7b26fdSDave Jones 	if (data == NULL) {
202bc7b26fdSDave Jones 		ret = -EINVAL;
203bc7b26fdSDave Jones 		goto error_get_fail;
204bc7b26fdSDave Jones 	}
205bc7b26fdSDave Jones 
2061da177e4SLinus Torvalds 	if ((ret = sysfs_create_group(&data->kobj, &stats_attr_group)))
2071da177e4SLinus Torvalds 		goto error_out;
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	stat->cpu = cpu;
2107a6aedfaSMike Travis 	per_cpu(cpufreq_stats_table, cpu) = stat;
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 	for (i=0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
2131da177e4SLinus Torvalds 		unsigned int freq = table[i].frequency;
2141da177e4SLinus Torvalds 		if (freq == CPUFREQ_ENTRY_INVALID)
2151da177e4SLinus Torvalds 			continue;
2161da177e4SLinus Torvalds 		count++;
2171da177e4SLinus Torvalds 	}
2181da177e4SLinus Torvalds 
21958f1df25SVenkatesh Pallipadi 	alloc_size = count * sizeof(int) + count * sizeof(cputime64_t);
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
2221da177e4SLinus Torvalds 	alloc_size += count * count * sizeof(int);
2231da177e4SLinus Torvalds #endif
2241da177e4SLinus Torvalds 	stat->max_state = count;
225e98df50cSDave Jones 	stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
2261da177e4SLinus Torvalds 	if (!stat->time_in_state) {
2271da177e4SLinus Torvalds 		ret = -ENOMEM;
2281da177e4SLinus Torvalds 		goto error_out;
2291da177e4SLinus Torvalds 	}
2301da177e4SLinus Torvalds 	stat->freq_table = (unsigned int *)(stat->time_in_state + count);
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
2331da177e4SLinus Torvalds 	stat->trans_table = stat->freq_table + count;
2341da177e4SLinus Torvalds #endif
2351da177e4SLinus Torvalds 	j = 0;
2361da177e4SLinus Torvalds 	for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
2371da177e4SLinus Torvalds 		unsigned int freq = table[i].frequency;
2381da177e4SLinus Torvalds 		if (freq == CPUFREQ_ENTRY_INVALID)
2391da177e4SLinus Torvalds 			continue;
2401da177e4SLinus Torvalds 		if (freq_table_get_index(stat, freq) == -1)
2411da177e4SLinus Torvalds 			stat->freq_table[j++] = freq;
2421da177e4SLinus Torvalds 	}
2431da177e4SLinus Torvalds 	stat->state_num = j;
2441da177e4SLinus Torvalds 	spin_lock(&cpufreq_stats_lock);
24558f1df25SVenkatesh Pallipadi 	stat->last_time = get_jiffies_64();
2461da177e4SLinus Torvalds 	stat->last_index = freq_table_get_index(stat, policy->cur);
2471da177e4SLinus Torvalds 	spin_unlock(&cpufreq_stats_lock);
2481da177e4SLinus Torvalds 	cpufreq_cpu_put(data);
2491da177e4SLinus Torvalds 	return 0;
2501da177e4SLinus Torvalds error_out:
2511da177e4SLinus Torvalds 	cpufreq_cpu_put(data);
252b7fb358cSDave Jones error_get_fail:
2531da177e4SLinus Torvalds 	kfree(stat);
2547a6aedfaSMike Travis 	per_cpu(cpufreq_stats_table, cpu) = NULL;
2551da177e4SLinus Torvalds 	return ret;
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds static int
2591da177e4SLinus Torvalds cpufreq_stat_notifier_policy (struct notifier_block *nb, unsigned long val,
2601da177e4SLinus Torvalds 		void *data)
2611da177e4SLinus Torvalds {
2621da177e4SLinus Torvalds 	int ret;
2631da177e4SLinus Torvalds 	struct cpufreq_policy *policy = data;
2641da177e4SLinus Torvalds 	struct cpufreq_frequency_table *table;
2651da177e4SLinus Torvalds 	unsigned int cpu = policy->cpu;
2661da177e4SLinus Torvalds 	if (val != CPUFREQ_NOTIFY)
2671da177e4SLinus Torvalds 		return 0;
2681da177e4SLinus Torvalds 	table = cpufreq_frequency_get_table(cpu);
2691da177e4SLinus Torvalds 	if (!table)
2701da177e4SLinus Torvalds 		return 0;
2711da177e4SLinus Torvalds 	if ((ret = cpufreq_stats_create_table(policy, table)))
2721da177e4SLinus Torvalds 		return ret;
2731da177e4SLinus Torvalds 	return 0;
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds static int
2771da177e4SLinus Torvalds cpufreq_stat_notifier_trans (struct notifier_block *nb, unsigned long val,
2781da177e4SLinus Torvalds 		void *data)
2791da177e4SLinus Torvalds {
2801da177e4SLinus Torvalds 	struct cpufreq_freqs *freq = data;
2811da177e4SLinus Torvalds 	struct cpufreq_stats *stat;
2821da177e4SLinus Torvalds 	int old_index, new_index;
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds 	if (val != CPUFREQ_POSTCHANGE)
2851da177e4SLinus Torvalds 		return 0;
2861da177e4SLinus Torvalds 
2877a6aedfaSMike Travis 	stat = per_cpu(cpufreq_stats_table, freq->cpu);
2881da177e4SLinus Torvalds 	if (!stat)
2891da177e4SLinus Torvalds 		return 0;
2908edc59d9SVenkatesh Pallipadi 
2916501faf8SShaohua Li 	old_index = stat->last_index;
2921da177e4SLinus Torvalds 	new_index = freq_table_get_index(stat, freq->new);
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds 	cpufreq_stats_update(freq->cpu);
2951da177e4SLinus Torvalds 	if (old_index == new_index)
2961da177e4SLinus Torvalds 		return 0;
2971da177e4SLinus Torvalds 
2988edc59d9SVenkatesh Pallipadi 	if (old_index == -1 || new_index == -1)
2998edc59d9SVenkatesh Pallipadi 		return 0;
3008edc59d9SVenkatesh Pallipadi 
3011da177e4SLinus Torvalds 	spin_lock(&cpufreq_stats_lock);
3021da177e4SLinus Torvalds 	stat->last_index = new_index;
3031da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
3041da177e4SLinus Torvalds 	stat->trans_table[old_index * stat->max_state + new_index]++;
3051da177e4SLinus Torvalds #endif
3061da177e4SLinus Torvalds 	stat->total_trans++;
3071da177e4SLinus Torvalds 	spin_unlock(&cpufreq_stats_lock);
3081da177e4SLinus Torvalds 	return 0;
3091da177e4SLinus Torvalds }
3101da177e4SLinus Torvalds 
31155395ae7SSatyam Sharma static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
31255395ae7SSatyam Sharma 					       unsigned long action,
31355395ae7SSatyam Sharma 					       void *hcpu)
314c32b6b8eSAshok Raj {
315c32b6b8eSAshok Raj 	unsigned int cpu = (unsigned long)hcpu;
316c32b6b8eSAshok Raj 
317c32b6b8eSAshok Raj 	switch (action) {
318c32b6b8eSAshok Raj 	case CPU_ONLINE:
3198bb78442SRafael J. Wysocki 	case CPU_ONLINE_FROZEN:
320c32b6b8eSAshok Raj 		cpufreq_update_policy(cpu);
321c32b6b8eSAshok Raj 		break;
322c32b6b8eSAshok Raj 	case CPU_DEAD:
3238bb78442SRafael J. Wysocki 	case CPU_DEAD_FROZEN:
324c32b6b8eSAshok Raj 		cpufreq_stats_free_table(cpu);
325c32b6b8eSAshok Raj 		break;
326c32b6b8eSAshok Raj 	}
327c32b6b8eSAshok Raj 	return NOTIFY_OK;
328c32b6b8eSAshok Raj }
329c32b6b8eSAshok Raj 
330f6ebef30SSam Ravnborg static struct notifier_block cpufreq_stat_cpu_notifier __refdata =
331c32b6b8eSAshok Raj {
332c32b6b8eSAshok Raj 	.notifier_call = cpufreq_stat_cpu_callback,
333c32b6b8eSAshok Raj };
334c32b6b8eSAshok Raj 
3351da177e4SLinus Torvalds static struct notifier_block notifier_policy_block = {
3361da177e4SLinus Torvalds 	.notifier_call = cpufreq_stat_notifier_policy
3371da177e4SLinus Torvalds };
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds static struct notifier_block notifier_trans_block = {
3401da177e4SLinus Torvalds 	.notifier_call = cpufreq_stat_notifier_trans
3411da177e4SLinus Torvalds };
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds static int
3441da177e4SLinus Torvalds __init cpufreq_stats_init(void)
3451da177e4SLinus Torvalds {
3461da177e4SLinus Torvalds 	int ret;
3471da177e4SLinus Torvalds 	unsigned int cpu;
348c32b6b8eSAshok Raj 
3491da177e4SLinus Torvalds 	spin_lock_init(&cpufreq_stats_lock);
3501da177e4SLinus Torvalds 	if ((ret = cpufreq_register_notifier(&notifier_policy_block,
3511da177e4SLinus Torvalds 				CPUFREQ_POLICY_NOTIFIER)))
3521da177e4SLinus Torvalds 		return ret;
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds 	if ((ret = cpufreq_register_notifier(&notifier_trans_block,
3551da177e4SLinus Torvalds 				CPUFREQ_TRANSITION_NOTIFIER))) {
3561da177e4SLinus Torvalds 		cpufreq_unregister_notifier(&notifier_policy_block,
3571da177e4SLinus Torvalds 				CPUFREQ_POLICY_NOTIFIER);
3581da177e4SLinus Torvalds 		return ret;
3591da177e4SLinus Torvalds 	}
3601da177e4SLinus Torvalds 
36165edc68cSChandra Seetharaman 	register_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
362c32b6b8eSAshok Raj 	for_each_online_cpu(cpu) {
36355395ae7SSatyam Sharma 		cpufreq_update_policy(cpu);
364c32b6b8eSAshok Raj 	}
3651da177e4SLinus Torvalds 	return 0;
3661da177e4SLinus Torvalds }
3671da177e4SLinus Torvalds static void
3681da177e4SLinus Torvalds __exit cpufreq_stats_exit(void)
3691da177e4SLinus Torvalds {
3701da177e4SLinus Torvalds 	unsigned int cpu;
371c32b6b8eSAshok Raj 
3721da177e4SLinus Torvalds 	cpufreq_unregister_notifier(&notifier_policy_block,
3731da177e4SLinus Torvalds 			CPUFREQ_POLICY_NOTIFIER);
3741da177e4SLinus Torvalds 	cpufreq_unregister_notifier(&notifier_trans_block,
3751da177e4SLinus Torvalds 			CPUFREQ_TRANSITION_NOTIFIER);
37665edc68cSChandra Seetharaman 	unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
377c32b6b8eSAshok Raj 	for_each_online_cpu(cpu) {
37855395ae7SSatyam Sharma 		cpufreq_stats_free_table(cpu);
379c32b6b8eSAshok Raj 	}
3801da177e4SLinus Torvalds }
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds MODULE_AUTHOR ("Zou Nan hai <nanhai.zou@intel.com>");
383e08f5f5bSGautham R Shenoy MODULE_DESCRIPTION ("'cpufreq_stats' - A driver to export cpufreq stats "
384e08f5f5bSGautham R Shenoy 				"through sysfs filesystem");
3851da177e4SLinus Torvalds MODULE_LICENSE ("GPL");
3861da177e4SLinus Torvalds 
3871da177e4SLinus Torvalds module_init(cpufreq_stats_init);
3881da177e4SLinus Torvalds module_exit(cpufreq_stats_exit);
389