18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SFI Performance States Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com> 68c2ecf20Sopenharmony_ci * Author: Srinidhi Kasagar <srinidhi.kasagar@intel.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/sfi.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/smp.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <asm/msr.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic struct cpufreq_frequency_table *freq_table; 208c2ecf20Sopenharmony_cistatic struct sfi_freq_table_entry *sfi_cpufreq_array; 218c2ecf20Sopenharmony_cistatic int num_freq_table_entries; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic int sfi_parse_freq(struct sfi_table_header *table) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct sfi_table_simple *sb; 268c2ecf20Sopenharmony_ci struct sfi_freq_table_entry *pentry; 278c2ecf20Sopenharmony_ci int totallen; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci sb = (struct sfi_table_simple *)table; 308c2ecf20Sopenharmony_ci num_freq_table_entries = SFI_GET_NUM_ENTRIES(sb, 318c2ecf20Sopenharmony_ci struct sfi_freq_table_entry); 328c2ecf20Sopenharmony_ci if (num_freq_table_entries <= 1) { 338c2ecf20Sopenharmony_ci pr_err("No p-states discovered\n"); 348c2ecf20Sopenharmony_ci return -ENODEV; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci pentry = (struct sfi_freq_table_entry *)sb->pentry; 388c2ecf20Sopenharmony_ci totallen = num_freq_table_entries * sizeof(*pentry); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci sfi_cpufreq_array = kmemdup(pentry, totallen, GFP_KERNEL); 418c2ecf20Sopenharmony_ci if (!sfi_cpufreq_array) 428c2ecf20Sopenharmony_ci return -ENOMEM; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return 0; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int sfi_cpufreq_target(struct cpufreq_policy *policy, unsigned int index) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci unsigned int next_perf_state = 0; /* Index into perf table */ 508c2ecf20Sopenharmony_ci u32 lo, hi; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci next_perf_state = policy->freq_table[index].driver_data; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci rdmsr_on_cpu(policy->cpu, MSR_IA32_PERF_CTL, &lo, &hi); 558c2ecf20Sopenharmony_ci lo = (lo & ~INTEL_PERF_CTL_MASK) | 568c2ecf20Sopenharmony_ci ((u32) sfi_cpufreq_array[next_perf_state].ctrl_val & 578c2ecf20Sopenharmony_ci INTEL_PERF_CTL_MASK); 588c2ecf20Sopenharmony_ci wrmsr_on_cpu(policy->cpu, MSR_IA32_PERF_CTL, lo, hi); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int sfi_cpufreq_cpu_init(struct cpufreq_policy *policy) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci policy->shared_type = CPUFREQ_SHARED_TYPE_HW; 668c2ecf20Sopenharmony_ci policy->cpuinfo.transition_latency = 100000; /* 100us */ 678c2ecf20Sopenharmony_ci policy->freq_table = freq_table; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic struct cpufreq_driver sfi_cpufreq_driver = { 738c2ecf20Sopenharmony_ci .flags = CPUFREQ_CONST_LOOPS, 748c2ecf20Sopenharmony_ci .verify = cpufreq_generic_frequency_table_verify, 758c2ecf20Sopenharmony_ci .target_index = sfi_cpufreq_target, 768c2ecf20Sopenharmony_ci .init = sfi_cpufreq_cpu_init, 778c2ecf20Sopenharmony_ci .name = "sfi-cpufreq", 788c2ecf20Sopenharmony_ci .attr = cpufreq_generic_attr, 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int __init sfi_cpufreq_init(void) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci int ret, i; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* parse the freq table from SFI */ 868c2ecf20Sopenharmony_ci ret = sfi_table_parse(SFI_SIG_FREQ, NULL, NULL, sfi_parse_freq); 878c2ecf20Sopenharmony_ci if (ret) 888c2ecf20Sopenharmony_ci return ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci freq_table = kcalloc(num_freq_table_entries + 1, sizeof(*freq_table), 918c2ecf20Sopenharmony_ci GFP_KERNEL); 928c2ecf20Sopenharmony_ci if (!freq_table) { 938c2ecf20Sopenharmony_ci ret = -ENOMEM; 948c2ecf20Sopenharmony_ci goto err_free_array; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci for (i = 0; i < num_freq_table_entries; i++) { 988c2ecf20Sopenharmony_ci freq_table[i].driver_data = i; 998c2ecf20Sopenharmony_ci freq_table[i].frequency = sfi_cpufreq_array[i].freq_mhz * 1000; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci freq_table[i].frequency = CPUFREQ_TABLE_END; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci ret = cpufreq_register_driver(&sfi_cpufreq_driver); 1048c2ecf20Sopenharmony_ci if (ret) 1058c2ecf20Sopenharmony_ci goto err_free_tbl; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return ret; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cierr_free_tbl: 1108c2ecf20Sopenharmony_ci kfree(freq_table); 1118c2ecf20Sopenharmony_cierr_free_array: 1128c2ecf20Sopenharmony_ci kfree(sfi_cpufreq_array); 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_cilate_initcall(sfi_cpufreq_init); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void __exit sfi_cpufreq_exit(void) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci cpufreq_unregister_driver(&sfi_cpufreq_driver); 1208c2ecf20Sopenharmony_ci kfree(freq_table); 1218c2ecf20Sopenharmony_ci kfree(sfi_cpufreq_array); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_cimodule_exit(sfi_cpufreq_exit); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>"); 1268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SFI Performance-States Driver"); 1278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 128