| Srinidhi Kasagar | e7ddf4b | 2014-12-19 23:13:51 +0530 | [diff] [blame] | 1 | /* | 
 | 2 |  *  SFI Performance States Driver | 
 | 3 |  * | 
 | 4 |  *  This program is free software; you can redistribute it and/or modify | 
 | 5 |  *  it under the terms of the GNU General Public License as published by | 
 | 6 |  *  the Free Software Foundation; either version 2 of the License. | 
 | 7 |  * | 
 | 8 |  *  This program is distributed in the hope that it will be useful, but | 
 | 9 |  *  WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 10 |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 | 11 |  *  General Public License for more details. | 
 | 12 |  * | 
 | 13 |  *  Author: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com> | 
 | 14 |  *  Author: Srinidhi Kasagar <srinidhi.kasagar@intel.com> | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #include <linux/cpufreq.h> | 
 | 18 | #include <linux/init.h> | 
 | 19 | #include <linux/kernel.h> | 
 | 20 | #include <linux/module.h> | 
 | 21 | #include <linux/sfi.h> | 
 | 22 | #include <linux/slab.h> | 
 | 23 | #include <linux/smp.h> | 
 | 24 |  | 
 | 25 | #include <asm/msr.h> | 
 | 26 |  | 
 | 27 | struct cpufreq_frequency_table *freq_table; | 
 | 28 | static struct sfi_freq_table_entry *sfi_cpufreq_array; | 
 | 29 | static int num_freq_table_entries; | 
 | 30 |  | 
 | 31 | static int sfi_parse_freq(struct sfi_table_header *table) | 
 | 32 | { | 
 | 33 | 	struct sfi_table_simple *sb; | 
 | 34 | 	struct sfi_freq_table_entry *pentry; | 
 | 35 | 	int totallen; | 
 | 36 |  | 
 | 37 | 	sb = (struct sfi_table_simple *)table; | 
 | 38 | 	num_freq_table_entries = SFI_GET_NUM_ENTRIES(sb, | 
 | 39 | 			struct sfi_freq_table_entry); | 
 | 40 | 	if (num_freq_table_entries <= 1) { | 
 | 41 | 		pr_err("No p-states discovered\n"); | 
 | 42 | 		return -ENODEV; | 
 | 43 | 	} | 
 | 44 |  | 
 | 45 | 	pentry = (struct sfi_freq_table_entry *)sb->pentry; | 
 | 46 | 	totallen = num_freq_table_entries * sizeof(*pentry); | 
 | 47 |  | 
| Andrzej Hajda | a482e55 | 2015-08-07 09:59:17 +0200 | [diff] [blame] | 48 | 	sfi_cpufreq_array = kmemdup(pentry, totallen, GFP_KERNEL); | 
| Srinidhi Kasagar | e7ddf4b | 2014-12-19 23:13:51 +0530 | [diff] [blame] | 49 | 	if (!sfi_cpufreq_array) | 
 | 50 | 		return -ENOMEM; | 
 | 51 |  | 
| Srinidhi Kasagar | e7ddf4b | 2014-12-19 23:13:51 +0530 | [diff] [blame] | 52 | 	return 0; | 
 | 53 | } | 
 | 54 |  | 
 | 55 | static int sfi_cpufreq_target(struct cpufreq_policy *policy, unsigned int index) | 
 | 56 | { | 
 | 57 | 	unsigned int next_perf_state = 0; /* Index into perf table */ | 
 | 58 | 	u32 lo, hi; | 
 | 59 |  | 
 | 60 | 	next_perf_state = policy->freq_table[index].driver_data; | 
 | 61 |  | 
 | 62 | 	rdmsr_on_cpu(policy->cpu, MSR_IA32_PERF_CTL, &lo, &hi); | 
 | 63 | 	lo = (lo & ~INTEL_PERF_CTL_MASK) | | 
 | 64 | 		((u32) sfi_cpufreq_array[next_perf_state].ctrl_val & | 
 | 65 | 		INTEL_PERF_CTL_MASK); | 
 | 66 | 	wrmsr_on_cpu(policy->cpu, MSR_IA32_PERF_CTL, lo, hi); | 
 | 67 |  | 
 | 68 | 	return 0; | 
 | 69 | } | 
 | 70 |  | 
 | 71 | static int sfi_cpufreq_cpu_init(struct cpufreq_policy *policy) | 
 | 72 | { | 
 | 73 | 	policy->shared_type = CPUFREQ_SHARED_TYPE_HW; | 
 | 74 | 	policy->cpuinfo.transition_latency = 100000;	/* 100us */ | 
 | 75 |  | 
 | 76 | 	return cpufreq_table_validate_and_show(policy, freq_table); | 
 | 77 | } | 
 | 78 |  | 
 | 79 | static struct cpufreq_driver sfi_cpufreq_driver = { | 
 | 80 | 	.flags		= CPUFREQ_CONST_LOOPS, | 
 | 81 | 	.verify		= cpufreq_generic_frequency_table_verify, | 
 | 82 | 	.target_index	= sfi_cpufreq_target, | 
 | 83 | 	.init		= sfi_cpufreq_cpu_init, | 
 | 84 | 	.name		= "sfi-cpufreq", | 
 | 85 | 	.attr		= cpufreq_generic_attr, | 
 | 86 | }; | 
 | 87 |  | 
 | 88 | static int __init sfi_cpufreq_init(void) | 
 | 89 | { | 
 | 90 | 	int ret, i; | 
 | 91 |  | 
 | 92 | 	/* parse the freq table from SFI */ | 
 | 93 | 	ret = sfi_table_parse(SFI_SIG_FREQ, NULL, NULL, sfi_parse_freq); | 
 | 94 | 	if (ret) | 
 | 95 | 		return ret; | 
 | 96 |  | 
 | 97 | 	freq_table = kzalloc(sizeof(*freq_table) * | 
 | 98 | 			(num_freq_table_entries + 1), GFP_KERNEL); | 
 | 99 | 	if (!freq_table) { | 
 | 100 | 		ret = -ENOMEM; | 
 | 101 | 		goto err_free_array; | 
 | 102 | 	} | 
 | 103 |  | 
 | 104 | 	for (i = 0; i < num_freq_table_entries; i++) { | 
 | 105 | 		freq_table[i].driver_data = i; | 
 | 106 | 		freq_table[i].frequency = sfi_cpufreq_array[i].freq_mhz * 1000; | 
 | 107 | 	} | 
 | 108 | 	freq_table[i].frequency = CPUFREQ_TABLE_END; | 
 | 109 |  | 
 | 110 | 	ret = cpufreq_register_driver(&sfi_cpufreq_driver); | 
 | 111 | 	if (ret) | 
 | 112 | 		goto err_free_tbl; | 
 | 113 |  | 
 | 114 | 	return ret; | 
 | 115 |  | 
 | 116 | err_free_tbl: | 
 | 117 | 	kfree(freq_table); | 
 | 118 | err_free_array: | 
 | 119 | 	kfree(sfi_cpufreq_array); | 
 | 120 | 	return ret; | 
 | 121 | } | 
 | 122 | late_initcall(sfi_cpufreq_init); | 
 | 123 |  | 
 | 124 | static void __exit sfi_cpufreq_exit(void) | 
 | 125 | { | 
 | 126 | 	cpufreq_unregister_driver(&sfi_cpufreq_driver); | 
 | 127 | 	kfree(freq_table); | 
 | 128 | 	kfree(sfi_cpufreq_array); | 
 | 129 | } | 
 | 130 | module_exit(sfi_cpufreq_exit); | 
 | 131 |  | 
 | 132 | MODULE_AUTHOR("Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>"); | 
 | 133 | MODULE_DESCRIPTION("SFI Performance-States Driver"); | 
 | 134 | MODULE_LICENSE("GPL"); |