2 * SFI Performance States Driver
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.
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.
13 * Author: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
14 * Author: Srinidhi Kasagar <srinidhi.kasagar@intel.com>
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>
27 struct cpufreq_frequency_table
*freq_table
;
28 static struct sfi_freq_table_entry
*sfi_cpufreq_array
;
29 static int num_freq_table_entries
;
31 static int sfi_parse_freq(struct sfi_table_header
*table
)
33 struct sfi_table_simple
*sb
;
34 struct sfi_freq_table_entry
*pentry
;
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");
45 pentry
= (struct sfi_freq_table_entry
*)sb
->pentry
;
46 totallen
= num_freq_table_entries
* sizeof(*pentry
);
48 sfi_cpufreq_array
= kzalloc(totallen
, GFP_KERNEL
);
49 if (!sfi_cpufreq_array
)
52 memcpy(sfi_cpufreq_array
, pentry
, totallen
);
57 static int sfi_cpufreq_target(struct cpufreq_policy
*policy
, unsigned int index
)
59 unsigned int next_perf_state
= 0; /* Index into perf table */
62 next_perf_state
= policy
->freq_table
[index
].driver_data
;
64 rdmsr_on_cpu(policy
->cpu
, MSR_IA32_PERF_CTL
, &lo
, &hi
);
65 lo
= (lo
& ~INTEL_PERF_CTL_MASK
) |
66 ((u32
) sfi_cpufreq_array
[next_perf_state
].ctrl_val
&
68 wrmsr_on_cpu(policy
->cpu
, MSR_IA32_PERF_CTL
, lo
, hi
);
73 static int sfi_cpufreq_cpu_init(struct cpufreq_policy
*policy
)
75 policy
->shared_type
= CPUFREQ_SHARED_TYPE_HW
;
76 policy
->cpuinfo
.transition_latency
= 100000; /* 100us */
78 return cpufreq_table_validate_and_show(policy
, freq_table
);
81 static struct cpufreq_driver sfi_cpufreq_driver
= {
82 .flags
= CPUFREQ_CONST_LOOPS
,
83 .verify
= cpufreq_generic_frequency_table_verify
,
84 .target_index
= sfi_cpufreq_target
,
85 .init
= sfi_cpufreq_cpu_init
,
86 .name
= "sfi-cpufreq",
87 .attr
= cpufreq_generic_attr
,
90 static int __init
sfi_cpufreq_init(void)
94 /* parse the freq table from SFI */
95 ret
= sfi_table_parse(SFI_SIG_FREQ
, NULL
, NULL
, sfi_parse_freq
);
99 freq_table
= kzalloc(sizeof(*freq_table
) *
100 (num_freq_table_entries
+ 1), GFP_KERNEL
);
106 for (i
= 0; i
< num_freq_table_entries
; i
++) {
107 freq_table
[i
].driver_data
= i
;
108 freq_table
[i
].frequency
= sfi_cpufreq_array
[i
].freq_mhz
* 1000;
110 freq_table
[i
].frequency
= CPUFREQ_TABLE_END
;
112 ret
= cpufreq_register_driver(&sfi_cpufreq_driver
);
121 kfree(sfi_cpufreq_array
);
124 late_initcall(sfi_cpufreq_init
);
126 static void __exit
sfi_cpufreq_exit(void)
128 cpufreq_unregister_driver(&sfi_cpufreq_driver
);
130 kfree(sfi_cpufreq_array
);
132 module_exit(sfi_cpufreq_exit
);
134 MODULE_AUTHOR("Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>");
135 MODULE_DESCRIPTION("SFI Performance-States Driver");
136 MODULE_LICENSE("GPL");