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>
135a0e3ad6STejun Heo #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/cpu.h>
151da177e4SLinus Torvalds #include <linux/sysfs.h>
161da177e4SLinus Torvalds #include <linux/cpufreq.h>
175c720d37SPaul Gortmaker #include <linux/module.h>
181da177e4SLinus Torvalds #include <linux/jiffies.h>
191da177e4SLinus Torvalds #include <linux/percpu.h>
201da177e4SLinus Torvalds #include <linux/kobject.h>
211da177e4SLinus Torvalds #include <linux/spinlock.h>
22c32b6b8eSAshok Raj #include <linux/notifier.h>
2358f1df25SVenkatesh Pallipadi #include <asm/cputime.h>
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds static spinlock_t cpufreq_stats_lock;
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds #define CPUFREQ_STATDEVICE_ATTR(_name, _mode, _show) \
281da177e4SLinus Torvalds static struct freq_attr _attr_##_name = {\
297b595756STejun Heo 	.attr = {.name = __stringify(_name), .mode = _mode, }, \
301da177e4SLinus Torvalds 	.show = _show,\
311da177e4SLinus Torvalds };
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds struct cpufreq_stats {
341da177e4SLinus Torvalds 	unsigned int cpu;
351da177e4SLinus Torvalds 	unsigned int total_trans;
361da177e4SLinus Torvalds 	unsigned long long  last_time;
371da177e4SLinus Torvalds 	unsigned int max_state;
381da177e4SLinus Torvalds 	unsigned int state_num;
391da177e4SLinus Torvalds 	unsigned int last_index;
401e7586a1SViresh Kumar 	u64 *time_in_state;
411da177e4SLinus Torvalds 	unsigned int *freq_table;
421da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
431da177e4SLinus Torvalds 	unsigned int *trans_table;
441da177e4SLinus Torvalds #endif
451da177e4SLinus Torvalds };
461da177e4SLinus Torvalds 
477a6aedfaSMike Travis static DEFINE_PER_CPU(struct cpufreq_stats *, cpufreq_stats_table);
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds struct cpufreq_stats_attribute {
501da177e4SLinus Torvalds 	struct attribute attr;
511da177e4SLinus Torvalds 	ssize_t(*show) (struct cpufreq_stats *, char *);
521da177e4SLinus Torvalds };
531da177e4SLinus Torvalds 
540a829c5aSDave Jones static int 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)
6364861634SMartin Schwidefsky 		stat->time_in_state[stat->last_index] +=
6464861634SMartin Schwidefsky 			cur_time - stat->last_time;
6558f1df25SVenkatesh Pallipadi 	stat->last_time = cur_time;
661da177e4SLinus Torvalds 	spin_unlock(&cpufreq_stats_lock);
671da177e4SLinus Torvalds 	return 0;
681da177e4SLinus Torvalds }
691da177e4SLinus Torvalds 
700a829c5aSDave Jones static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
711da177e4SLinus Torvalds {
727a6aedfaSMike Travis 	struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
731da177e4SLinus Torvalds 	if (!stat)
741da177e4SLinus Torvalds 		return 0;
751da177e4SLinus Torvalds 	return sprintf(buf, "%d\n",
767a6aedfaSMike Travis 			per_cpu(cpufreq_stats_table, stat->cpu)->total_trans);
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
790a829c5aSDave Jones static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
801da177e4SLinus Torvalds {
811da177e4SLinus Torvalds 	ssize_t len = 0;
821da177e4SLinus Torvalds 	int i;
837a6aedfaSMike Travis 	struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
841da177e4SLinus Torvalds 	if (!stat)
851da177e4SLinus Torvalds 		return 0;
861da177e4SLinus Torvalds 	cpufreq_stats_update(stat->cpu);
871da177e4SLinus Torvalds 	for (i = 0; i < stat->state_num; i++) {
8858f1df25SVenkatesh Pallipadi 		len += sprintf(buf + len, "%u %llu\n", stat->freq_table[i],
890a829c5aSDave Jones 			(unsigned long long)
900a829c5aSDave Jones 			cputime64_to_clock_t(stat->time_in_state[i]));
911da177e4SLinus Torvalds 	}
921da177e4SLinus Torvalds 	return len;
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
960a829c5aSDave Jones static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
971da177e4SLinus Torvalds {
981da177e4SLinus Torvalds 	ssize_t len = 0;
991da177e4SLinus Torvalds 	int i, j;
1001da177e4SLinus Torvalds 
1017a6aedfaSMike Travis 	struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
1021da177e4SLinus Torvalds 	if (!stat)
1031da177e4SLinus Torvalds 		return 0;
1041da177e4SLinus Torvalds 	cpufreq_stats_update(stat->cpu);
10558f1df25SVenkatesh Pallipadi 	len += snprintf(buf + len, PAGE_SIZE - len, "   From  :    To\n");
10658f1df25SVenkatesh Pallipadi 	len += snprintf(buf + len, PAGE_SIZE - len, "         : ");
1071da177e4SLinus Torvalds 	for (i = 0; i < stat->state_num; i++) {
1081da177e4SLinus Torvalds 		if (len >= PAGE_SIZE)
1091da177e4SLinus Torvalds 			break;
11058f1df25SVenkatesh Pallipadi 		len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
11158f1df25SVenkatesh Pallipadi 				stat->freq_table[i]);
11258f1df25SVenkatesh Pallipadi 	}
11358f1df25SVenkatesh Pallipadi 	if (len >= PAGE_SIZE)
11425aca347SCesar Eduardo Barros 		return PAGE_SIZE;
11558f1df25SVenkatesh Pallipadi 
11658f1df25SVenkatesh Pallipadi 	len += snprintf(buf + len, PAGE_SIZE - len, "\n");
11758f1df25SVenkatesh Pallipadi 
11858f1df25SVenkatesh Pallipadi 	for (i = 0; i < stat->state_num; i++) {
11958f1df25SVenkatesh Pallipadi 		if (len >= PAGE_SIZE)
12058f1df25SVenkatesh Pallipadi 			break;
12158f1df25SVenkatesh Pallipadi 
12258f1df25SVenkatesh Pallipadi 		len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ",
1231da177e4SLinus Torvalds 				stat->freq_table[i]);
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 		for (j = 0; j < stat->state_num; j++)   {
1261da177e4SLinus Torvalds 			if (len >= PAGE_SIZE)
1271da177e4SLinus Torvalds 				break;
12858f1df25SVenkatesh Pallipadi 			len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
1291da177e4SLinus Torvalds 					stat->trans_table[i*stat->max_state+j]);
1301da177e4SLinus Torvalds 		}
13125aca347SCesar Eduardo Barros 		if (len >= PAGE_SIZE)
13225aca347SCesar Eduardo Barros 			break;
1331da177e4SLinus Torvalds 		len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1341da177e4SLinus Torvalds 	}
13525aca347SCesar Eduardo Barros 	if (len >= PAGE_SIZE)
13625aca347SCesar Eduardo Barros 		return PAGE_SIZE;
1371da177e4SLinus Torvalds 	return len;
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds CPUFREQ_STATDEVICE_ATTR(trans_table, 0444, show_trans_table);
1401da177e4SLinus Torvalds #endif
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds CPUFREQ_STATDEVICE_ATTR(total_trans, 0444, show_total_trans);
1431da177e4SLinus Torvalds CPUFREQ_STATDEVICE_ATTR(time_in_state, 0444, show_time_in_state);
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds static struct attribute *default_attrs[] = {
1461da177e4SLinus Torvalds 	&_attr_total_trans.attr,
1471da177e4SLinus Torvalds 	&_attr_time_in_state.attr,
1481da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
1491da177e4SLinus Torvalds 	&_attr_trans_table.attr,
1501da177e4SLinus Torvalds #endif
1511da177e4SLinus Torvalds 	NULL
1521da177e4SLinus Torvalds };
1531da177e4SLinus Torvalds static struct attribute_group stats_attr_group = {
1541da177e4SLinus Torvalds 	.attrs = default_attrs,
1551da177e4SLinus Torvalds 	.name = "stats"
1561da177e4SLinus Torvalds };
1571da177e4SLinus Torvalds 
1580a829c5aSDave Jones static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
1591da177e4SLinus Torvalds {
1601da177e4SLinus Torvalds 	int index;
1611da177e4SLinus Torvalds 	for (index = 0; index < stat->max_state; index++)
1621da177e4SLinus Torvalds 		if (stat->freq_table[index] == freq)
1631da177e4SLinus Torvalds 			return index;
1641da177e4SLinus Torvalds 	return -1;
1651da177e4SLinus Torvalds }
1661da177e4SLinus Torvalds 
16798586ed8Ssteven finney /* should be called late in the CPU removal sequence so that the stats
16898586ed8Ssteven finney  * memory is still available in case someone tries to use it.
16998586ed8Ssteven finney  */
170a3323473SAdrian Bunk static void cpufreq_stats_free_table(unsigned int cpu)
1711da177e4SLinus Torvalds {
1727a6aedfaSMike Travis 	struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu);
173b8eed8afSViresh Kumar 
1741da177e4SLinus Torvalds 	if (stat) {
175b8eed8afSViresh Kumar 		pr_debug("%s: Free stat table\n", __func__);
1761da177e4SLinus Torvalds 		kfree(stat->time_in_state);
1771da177e4SLinus Torvalds 		kfree(stat);
1787a6aedfaSMike Travis 		per_cpu(cpufreq_stats_table, cpu) = NULL;
17998586ed8Ssteven finney 	}
180b8eed8afSViresh Kumar }
18198586ed8Ssteven finney 
18298586ed8Ssteven finney /* must be called early in the CPU removal sequence (before
18398586ed8Ssteven finney  * cpufreq_remove_dev) so that policy is still valid.
18498586ed8Ssteven finney  */
18598586ed8Ssteven finney static void cpufreq_stats_free_sysfs(unsigned int cpu)
18698586ed8Ssteven finney {
18798586ed8Ssteven finney 	struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
188b8eed8afSViresh Kumar 	if (policy && (cpumask_weight(policy->cpus) == 1)) {
189b8eed8afSViresh Kumar 		pr_debug("%s: Free sysfs stat\n", __func__);
19098586ed8Ssteven finney 		sysfs_remove_group(&policy->kobj, &stats_attr_group);
191b8eed8afSViresh Kumar 	}
1921da177e4SLinus Torvalds 	if (policy)
1931da177e4SLinus Torvalds 		cpufreq_cpu_put(policy);
1941da177e4SLinus Torvalds }
1951da177e4SLinus Torvalds 
1960a829c5aSDave Jones static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
1971da177e4SLinus Torvalds 		struct cpufreq_frequency_table *table)
1981da177e4SLinus Torvalds {
1991da177e4SLinus Torvalds 	unsigned int i, j, count = 0, ret = 0;
2001da177e4SLinus Torvalds 	struct cpufreq_stats *stat;
2011da177e4SLinus Torvalds 	struct cpufreq_policy *data;
2021da177e4SLinus Torvalds 	unsigned int alloc_size;
2031da177e4SLinus Torvalds 	unsigned int cpu = policy->cpu;
2047a6aedfaSMike Travis 	if (per_cpu(cpufreq_stats_table, cpu))
2051da177e4SLinus Torvalds 		return -EBUSY;
2060a829c5aSDave Jones 	stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL);
2070a829c5aSDave Jones 	if ((stat) == NULL)
2081da177e4SLinus Torvalds 		return -ENOMEM;
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds 	data = cpufreq_cpu_get(cpu);
211bc7b26fdSDave Jones 	if (data == NULL) {
212bc7b26fdSDave Jones 		ret = -EINVAL;
213bc7b26fdSDave Jones 		goto error_get_fail;
214bc7b26fdSDave Jones 	}
215bc7b26fdSDave Jones 
2160a829c5aSDave Jones 	ret = sysfs_create_group(&data->kobj, &stats_attr_group);
2170a829c5aSDave Jones 	if (ret)
2181da177e4SLinus Torvalds 		goto error_out;
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds 	stat->cpu = cpu;
2217a6aedfaSMike Travis 	per_cpu(cpufreq_stats_table, cpu) = stat;
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds 	for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
2241da177e4SLinus Torvalds 		unsigned int freq = table[i].frequency;
2251da177e4SLinus Torvalds 		if (freq == CPUFREQ_ENTRY_INVALID)
2261da177e4SLinus Torvalds 			continue;
2271da177e4SLinus Torvalds 		count++;
2281da177e4SLinus Torvalds 	}
2291da177e4SLinus Torvalds 
2301e7586a1SViresh Kumar 	alloc_size = count * sizeof(int) + count * sizeof(u64);
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
2331da177e4SLinus Torvalds 	alloc_size += count * count * sizeof(int);
2341da177e4SLinus Torvalds #endif
2351da177e4SLinus Torvalds 	stat->max_state = count;
236e98df50cSDave Jones 	stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
2371da177e4SLinus Torvalds 	if (!stat->time_in_state) {
2381da177e4SLinus Torvalds 		ret = -ENOMEM;
2391da177e4SLinus Torvalds 		goto error_out;
2401da177e4SLinus Torvalds 	}
2411da177e4SLinus Torvalds 	stat->freq_table = (unsigned int *)(stat->time_in_state + count);
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
2441da177e4SLinus Torvalds 	stat->trans_table = stat->freq_table + count;
2451da177e4SLinus Torvalds #endif
2461da177e4SLinus Torvalds 	j = 0;
2471da177e4SLinus Torvalds 	for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
2481da177e4SLinus Torvalds 		unsigned int freq = table[i].frequency;
2491da177e4SLinus Torvalds 		if (freq == CPUFREQ_ENTRY_INVALID)
2501da177e4SLinus Torvalds 			continue;
2511da177e4SLinus Torvalds 		if (freq_table_get_index(stat, freq) == -1)
2521da177e4SLinus Torvalds 			stat->freq_table[j++] = freq;
2531da177e4SLinus Torvalds 	}
2541da177e4SLinus Torvalds 	stat->state_num = j;
2551da177e4SLinus Torvalds 	spin_lock(&cpufreq_stats_lock);
25658f1df25SVenkatesh Pallipadi 	stat->last_time = get_jiffies_64();
2571da177e4SLinus Torvalds 	stat->last_index = freq_table_get_index(stat, policy->cur);
2581da177e4SLinus Torvalds 	spin_unlock(&cpufreq_stats_lock);
2591da177e4SLinus Torvalds 	cpufreq_cpu_put(data);
2601da177e4SLinus Torvalds 	return 0;
2611da177e4SLinus Torvalds error_out:
2621da177e4SLinus Torvalds 	cpufreq_cpu_put(data);
263b7fb358cSDave Jones error_get_fail:
2641da177e4SLinus Torvalds 	kfree(stat);
2657a6aedfaSMike Travis 	per_cpu(cpufreq_stats_table, cpu) = NULL;
2661da177e4SLinus Torvalds 	return ret;
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds 
269b8eed8afSViresh Kumar static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy)
270b8eed8afSViresh Kumar {
271b8eed8afSViresh Kumar 	struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table,
272b8eed8afSViresh Kumar 			policy->last_cpu);
273b8eed8afSViresh Kumar 
274b8eed8afSViresh Kumar 	pr_debug("Updating stats_table for new_cpu %u from last_cpu %u\n",
275b8eed8afSViresh Kumar 			policy->cpu, policy->last_cpu);
276b8eed8afSViresh Kumar 	per_cpu(cpufreq_stats_table, policy->cpu) = per_cpu(cpufreq_stats_table,
277b8eed8afSViresh Kumar 			policy->last_cpu);
278b8eed8afSViresh Kumar 	per_cpu(cpufreq_stats_table, policy->last_cpu) = NULL;
279b8eed8afSViresh Kumar 	stat->cpu = policy->cpu;
280b8eed8afSViresh Kumar }
281b8eed8afSViresh Kumar 
2820a829c5aSDave Jones static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
2830a829c5aSDave Jones 		unsigned long val, void *data)
2841da177e4SLinus Torvalds {
2851da177e4SLinus Torvalds 	int ret;
2861da177e4SLinus Torvalds 	struct cpufreq_policy *policy = data;
2871da177e4SLinus Torvalds 	struct cpufreq_frequency_table *table;
2881da177e4SLinus Torvalds 	unsigned int cpu = policy->cpu;
289b8eed8afSViresh Kumar 
290b8eed8afSViresh Kumar 	if (val == CPUFREQ_UPDATE_POLICY_CPU) {
291b8eed8afSViresh Kumar 		cpufreq_stats_update_policy_cpu(policy);
292b8eed8afSViresh Kumar 		return 0;
293b8eed8afSViresh Kumar 	}
294b8eed8afSViresh Kumar 
2951da177e4SLinus Torvalds 	if (val != CPUFREQ_NOTIFY)
2961da177e4SLinus Torvalds 		return 0;
2971da177e4SLinus Torvalds 	table = cpufreq_frequency_get_table(cpu);
2981da177e4SLinus Torvalds 	if (!table)
2991da177e4SLinus Torvalds 		return 0;
3000a829c5aSDave Jones 	ret = cpufreq_stats_create_table(policy, table);
3010a829c5aSDave Jones 	if (ret)
3021da177e4SLinus Torvalds 		return ret;
3031da177e4SLinus Torvalds 	return 0;
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
3060a829c5aSDave Jones static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
3070a829c5aSDave Jones 		unsigned long val, void *data)
3081da177e4SLinus Torvalds {
3091da177e4SLinus Torvalds 	struct cpufreq_freqs *freq = data;
3101da177e4SLinus Torvalds 	struct cpufreq_stats *stat;
3111da177e4SLinus Torvalds 	int old_index, new_index;
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	if (val != CPUFREQ_POSTCHANGE)
3141da177e4SLinus Torvalds 		return 0;
3151da177e4SLinus Torvalds 
3167a6aedfaSMike Travis 	stat = per_cpu(cpufreq_stats_table, freq->cpu);
3171da177e4SLinus Torvalds 	if (!stat)
3181da177e4SLinus Torvalds 		return 0;
3198edc59d9SVenkatesh Pallipadi 
3206501faf8SShaohua Li 	old_index = stat->last_index;
3211da177e4SLinus Torvalds 	new_index = freq_table_get_index(stat, freq->new);
3221da177e4SLinus Torvalds 
32346a310b8SKonrad Rzeszutek Wilk 	/* We can't do stat->time_in_state[-1]= .. */
32446a310b8SKonrad Rzeszutek Wilk 	if (old_index == -1 || new_index == -1)
3251da177e4SLinus Torvalds 		return 0;
3261da177e4SLinus Torvalds 
32746a310b8SKonrad Rzeszutek Wilk 	cpufreq_stats_update(freq->cpu);
32846a310b8SKonrad Rzeszutek Wilk 
32946a310b8SKonrad Rzeszutek Wilk 	if (old_index == new_index)
3308edc59d9SVenkatesh Pallipadi 		return 0;
3318edc59d9SVenkatesh Pallipadi 
3321da177e4SLinus Torvalds 	spin_lock(&cpufreq_stats_lock);
3331da177e4SLinus Torvalds 	stat->last_index = new_index;
3341da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
3351da177e4SLinus Torvalds 	stat->trans_table[old_index * stat->max_state + new_index]++;
3361da177e4SLinus Torvalds #endif
3371da177e4SLinus Torvalds 	stat->total_trans++;
3381da177e4SLinus Torvalds 	spin_unlock(&cpufreq_stats_lock);
3391da177e4SLinus Torvalds 	return 0;
3401da177e4SLinus Torvalds }
3411da177e4SLinus Torvalds 
34255395ae7SSatyam Sharma static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
34355395ae7SSatyam Sharma 					       unsigned long action,
34455395ae7SSatyam Sharma 					       void *hcpu)
345c32b6b8eSAshok Raj {
346c32b6b8eSAshok Raj 	unsigned int cpu = (unsigned long)hcpu;
347c32b6b8eSAshok Raj 
348c32b6b8eSAshok Raj 	switch (action) {
349c32b6b8eSAshok Raj 	case CPU_ONLINE:
3508bb78442SRafael J. Wysocki 	case CPU_ONLINE_FROZEN:
351c32b6b8eSAshok Raj 		cpufreq_update_policy(cpu);
352c32b6b8eSAshok Raj 		break;
35398586ed8Ssteven finney 	case CPU_DOWN_PREPARE:
354e3773677STu, Xiaobing 	case CPU_DOWN_PREPARE_FROZEN:
35598586ed8Ssteven finney 		cpufreq_stats_free_sysfs(cpu);
35698586ed8Ssteven finney 		break;
357c32b6b8eSAshok Raj 	case CPU_DEAD:
3588bb78442SRafael J. Wysocki 	case CPU_DEAD_FROZEN:
359c32b6b8eSAshok Raj 		cpufreq_stats_free_table(cpu);
360c32b6b8eSAshok Raj 		break;
361c32b6b8eSAshok Raj 	}
362c32b6b8eSAshok Raj 	return NOTIFY_OK;
363c32b6b8eSAshok Raj }
364c32b6b8eSAshok Raj 
36598586ed8Ssteven finney /* priority=1 so this will get called before cpufreq_remove_dev */
366469057d5SKarthigan Srinivasan static struct notifier_block cpufreq_stat_cpu_notifier __refdata = {
367c32b6b8eSAshok Raj 	.notifier_call = cpufreq_stat_cpu_callback,
36898586ed8Ssteven finney 	.priority = 1,
369c32b6b8eSAshok Raj };
370c32b6b8eSAshok Raj 
3711da177e4SLinus Torvalds static struct notifier_block notifier_policy_block = {
3721da177e4SLinus Torvalds 	.notifier_call = cpufreq_stat_notifier_policy
3731da177e4SLinus Torvalds };
3741da177e4SLinus Torvalds 
3751da177e4SLinus Torvalds static struct notifier_block notifier_trans_block = {
3761da177e4SLinus Torvalds 	.notifier_call = cpufreq_stat_notifier_trans
3771da177e4SLinus Torvalds };
3781da177e4SLinus Torvalds 
3790a829c5aSDave Jones static int __init cpufreq_stats_init(void)
3801da177e4SLinus Torvalds {
3811da177e4SLinus Torvalds 	int ret;
3821da177e4SLinus Torvalds 	unsigned int cpu;
383c32b6b8eSAshok Raj 
3841da177e4SLinus Torvalds 	spin_lock_init(&cpufreq_stats_lock);
3850a829c5aSDave Jones 	ret = cpufreq_register_notifier(&notifier_policy_block,
3860a829c5aSDave Jones 				CPUFREQ_POLICY_NOTIFIER);
3870a829c5aSDave Jones 	if (ret)
3881da177e4SLinus Torvalds 		return ret;
3891da177e4SLinus Torvalds 
39056836fb4SKonstantin Khlebnikov 	register_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
39156836fb4SKonstantin Khlebnikov 	for_each_online_cpu(cpu)
39256836fb4SKonstantin Khlebnikov 		cpufreq_update_policy(cpu);
39356836fb4SKonstantin Khlebnikov 
3940a829c5aSDave Jones 	ret = cpufreq_register_notifier(&notifier_trans_block,
3950a829c5aSDave Jones 				CPUFREQ_TRANSITION_NOTIFIER);
3960a829c5aSDave Jones 	if (ret) {
3971da177e4SLinus Torvalds 		cpufreq_unregister_notifier(&notifier_policy_block,
3981da177e4SLinus Torvalds 				CPUFREQ_POLICY_NOTIFIER);
39956836fb4SKonstantin Khlebnikov 		unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
40056836fb4SKonstantin Khlebnikov 		for_each_online_cpu(cpu)
40156836fb4SKonstantin Khlebnikov 			cpufreq_stats_free_table(cpu);
4021da177e4SLinus Torvalds 		return ret;
4031da177e4SLinus Torvalds 	}
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds 	return 0;
4061da177e4SLinus Torvalds }
4070a829c5aSDave Jones static void __exit cpufreq_stats_exit(void)
4081da177e4SLinus Torvalds {
4091da177e4SLinus Torvalds 	unsigned int cpu;
410c32b6b8eSAshok Raj 
4111da177e4SLinus Torvalds 	cpufreq_unregister_notifier(&notifier_policy_block,
4121da177e4SLinus Torvalds 			CPUFREQ_POLICY_NOTIFIER);
4131da177e4SLinus Torvalds 	cpufreq_unregister_notifier(&notifier_trans_block,
4141da177e4SLinus Torvalds 			CPUFREQ_TRANSITION_NOTIFIER);
41565edc68cSChandra Seetharaman 	unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
416c32b6b8eSAshok Raj 	for_each_online_cpu(cpu) {
41755395ae7SSatyam Sharma 		cpufreq_stats_free_table(cpu);
41813f06753SDave Jones 		cpufreq_stats_free_sysfs(cpu);
419c32b6b8eSAshok Raj 	}
4201da177e4SLinus Torvalds }
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");
423e08f5f5bSGautham R Shenoy MODULE_DESCRIPTION("'cpufreq_stats' - A driver to export cpufreq stats "
424e08f5f5bSGautham R Shenoy 				"through sysfs filesystem");
4251da177e4SLinus Torvalds MODULE_LICENSE("GPL");
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds module_init(cpufreq_stats_init);
4281da177e4SLinus Torvalds module_exit(cpufreq_stats_exit);
429