18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file provides the ACPI based P-state support. This 48c2ecf20Sopenharmony_ci * module works with generic cpufreq infrastructure. Most of 58c2ecf20Sopenharmony_ci * the code is based on i386 version 68c2ecf20Sopenharmony_ci * (arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c) 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2005 Intel Corp 98c2ecf20Sopenharmony_ci * Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 198c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 208c2ecf20Sopenharmony_ci#include <asm/io.h> 218c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 228c2ecf20Sopenharmony_ci#include <asm/pal.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/acpi.h> 258c2ecf20Sopenharmony_ci#include <acpi/processor.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Venkatesh Pallipadi"); 288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ACPI Processor P-States Driver"); 298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct cpufreq_acpi_io { 328c2ecf20Sopenharmony_ci struct acpi_processor_performance acpi_data; 338c2ecf20Sopenharmony_ci unsigned int resume; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct cpufreq_acpi_req { 378c2ecf20Sopenharmony_ci unsigned int cpu; 388c2ecf20Sopenharmony_ci unsigned int state; 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic struct cpufreq_acpi_io *acpi_io_data[NR_CPUS]; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic struct cpufreq_driver acpi_cpufreq_driver; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int 478c2ecf20Sopenharmony_ciprocessor_set_pstate ( 488c2ecf20Sopenharmony_ci u32 value) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci s64 retval; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci pr_debug("processor_set_pstate\n"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci retval = ia64_pal_set_pstate((u64)value); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (retval) { 578c2ecf20Sopenharmony_ci pr_debug("Failed to set freq to 0x%x, with error 0x%lx\n", 588c2ecf20Sopenharmony_ci value, retval); 598c2ecf20Sopenharmony_ci return -ENODEV; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci return (int)retval; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int 668c2ecf20Sopenharmony_ciprocessor_get_pstate ( 678c2ecf20Sopenharmony_ci u32 *value) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci u64 pstate_index = 0; 708c2ecf20Sopenharmony_ci s64 retval; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci pr_debug("processor_get_pstate\n"); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci retval = ia64_pal_get_pstate(&pstate_index, 758c2ecf20Sopenharmony_ci PAL_GET_PSTATE_TYPE_INSTANT); 768c2ecf20Sopenharmony_ci *value = (u32) pstate_index; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (retval) 798c2ecf20Sopenharmony_ci pr_debug("Failed to get current freq with " 808c2ecf20Sopenharmony_ci "error 0x%lx, idx 0x%x\n", retval, *value); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return (int)retval; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* To be used only after data->acpi_data is initialized */ 878c2ecf20Sopenharmony_cistatic unsigned 888c2ecf20Sopenharmony_ciextract_clock ( 898c2ecf20Sopenharmony_ci struct cpufreq_acpi_io *data, 908c2ecf20Sopenharmony_ci unsigned value) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci unsigned long i; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci pr_debug("extract_clock\n"); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci for (i = 0; i < data->acpi_data.state_count; i++) { 978c2ecf20Sopenharmony_ci if (value == data->acpi_data.states[i].status) 988c2ecf20Sopenharmony_ci return data->acpi_data.states[i].core_frequency; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci return data->acpi_data.states[i-1].core_frequency; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic long 1058c2ecf20Sopenharmony_ciprocessor_get_freq ( 1068c2ecf20Sopenharmony_ci void *arg) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct cpufreq_acpi_req *req = arg; 1098c2ecf20Sopenharmony_ci unsigned int cpu = req->cpu; 1108c2ecf20Sopenharmony_ci struct cpufreq_acpi_io *data = acpi_io_data[cpu]; 1118c2ecf20Sopenharmony_ci u32 value; 1128c2ecf20Sopenharmony_ci int ret; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci pr_debug("processor_get_freq\n"); 1158c2ecf20Sopenharmony_ci if (smp_processor_id() != cpu) 1168c2ecf20Sopenharmony_ci return -EAGAIN; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* processor_get_pstate gets the instantaneous frequency */ 1198c2ecf20Sopenharmony_ci ret = processor_get_pstate(&value); 1208c2ecf20Sopenharmony_ci if (ret) { 1218c2ecf20Sopenharmony_ci pr_warn("get performance failed with error %d\n", ret); 1228c2ecf20Sopenharmony_ci return ret; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci return 1000 * extract_clock(data, value); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic long 1298c2ecf20Sopenharmony_ciprocessor_set_freq ( 1308c2ecf20Sopenharmony_ci void *arg) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct cpufreq_acpi_req *req = arg; 1338c2ecf20Sopenharmony_ci unsigned int cpu = req->cpu; 1348c2ecf20Sopenharmony_ci struct cpufreq_acpi_io *data = acpi_io_data[cpu]; 1358c2ecf20Sopenharmony_ci int ret, state = req->state; 1368c2ecf20Sopenharmony_ci u32 value; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci pr_debug("processor_set_freq\n"); 1398c2ecf20Sopenharmony_ci if (smp_processor_id() != cpu) 1408c2ecf20Sopenharmony_ci return -EAGAIN; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (state == data->acpi_data.state) { 1438c2ecf20Sopenharmony_ci if (unlikely(data->resume)) { 1448c2ecf20Sopenharmony_ci pr_debug("Called after resume, resetting to P%d\n", state); 1458c2ecf20Sopenharmony_ci data->resume = 0; 1468c2ecf20Sopenharmony_ci } else { 1478c2ecf20Sopenharmony_ci pr_debug("Already at target state (P%d)\n", state); 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci pr_debug("Transitioning from P%d to P%d\n", 1538c2ecf20Sopenharmony_ci data->acpi_data.state, state); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* 1568c2ecf20Sopenharmony_ci * First we write the target state's 'control' value to the 1578c2ecf20Sopenharmony_ci * control_register. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci value = (u32) data->acpi_data.states[state].control; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci pr_debug("Transitioning to state: 0x%08x\n", value); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci ret = processor_set_pstate(value); 1648c2ecf20Sopenharmony_ci if (ret) { 1658c2ecf20Sopenharmony_ci pr_warn("Transition failed with error %d\n", ret); 1668c2ecf20Sopenharmony_ci return -ENODEV; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci data->acpi_data.state = state; 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic unsigned int 1758c2ecf20Sopenharmony_ciacpi_cpufreq_get ( 1768c2ecf20Sopenharmony_ci unsigned int cpu) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct cpufreq_acpi_req req; 1798c2ecf20Sopenharmony_ci long ret; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci req.cpu = cpu; 1828c2ecf20Sopenharmony_ci ret = work_on_cpu(cpu, processor_get_freq, &req); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return ret > 0 ? (unsigned int) ret : 0; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int 1898c2ecf20Sopenharmony_ciacpi_cpufreq_target ( 1908c2ecf20Sopenharmony_ci struct cpufreq_policy *policy, 1918c2ecf20Sopenharmony_ci unsigned int index) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct cpufreq_acpi_req req; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci req.cpu = policy->cpu; 1968c2ecf20Sopenharmony_ci req.state = index; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return work_on_cpu(req.cpu, processor_set_freq, &req); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int 2028c2ecf20Sopenharmony_ciacpi_cpufreq_cpu_init ( 2038c2ecf20Sopenharmony_ci struct cpufreq_policy *policy) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci unsigned int i; 2068c2ecf20Sopenharmony_ci unsigned int cpu = policy->cpu; 2078c2ecf20Sopenharmony_ci struct cpufreq_acpi_io *data; 2088c2ecf20Sopenharmony_ci unsigned int result = 0; 2098c2ecf20Sopenharmony_ci struct cpufreq_frequency_table *freq_table; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci pr_debug("acpi_cpufreq_cpu_init\n"); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 2148c2ecf20Sopenharmony_ci if (!data) 2158c2ecf20Sopenharmony_ci return (-ENOMEM); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci acpi_io_data[cpu] = data; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci result = acpi_processor_register_performance(&data->acpi_data, cpu); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (result) 2228c2ecf20Sopenharmony_ci goto err_free; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* capability check */ 2258c2ecf20Sopenharmony_ci if (data->acpi_data.state_count <= 1) { 2268c2ecf20Sopenharmony_ci pr_debug("No P-States\n"); 2278c2ecf20Sopenharmony_ci result = -ENODEV; 2288c2ecf20Sopenharmony_ci goto err_unreg; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if ((data->acpi_data.control_register.space_id != 2328c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_FIXED_HARDWARE) || 2338c2ecf20Sopenharmony_ci (data->acpi_data.status_register.space_id != 2348c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_FIXED_HARDWARE)) { 2358c2ecf20Sopenharmony_ci pr_debug("Unsupported address space [%d, %d]\n", 2368c2ecf20Sopenharmony_ci (u32) (data->acpi_data.control_register.space_id), 2378c2ecf20Sopenharmony_ci (u32) (data->acpi_data.status_register.space_id)); 2388c2ecf20Sopenharmony_ci result = -ENODEV; 2398c2ecf20Sopenharmony_ci goto err_unreg; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* alloc freq_table */ 2438c2ecf20Sopenharmony_ci freq_table = kcalloc(data->acpi_data.state_count + 1, 2448c2ecf20Sopenharmony_ci sizeof(*freq_table), 2458c2ecf20Sopenharmony_ci GFP_KERNEL); 2468c2ecf20Sopenharmony_ci if (!freq_table) { 2478c2ecf20Sopenharmony_ci result = -ENOMEM; 2488c2ecf20Sopenharmony_ci goto err_unreg; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* detect transition latency */ 2528c2ecf20Sopenharmony_ci policy->cpuinfo.transition_latency = 0; 2538c2ecf20Sopenharmony_ci for (i=0; i<data->acpi_data.state_count; i++) { 2548c2ecf20Sopenharmony_ci if ((data->acpi_data.states[i].transition_latency * 1000) > 2558c2ecf20Sopenharmony_ci policy->cpuinfo.transition_latency) { 2568c2ecf20Sopenharmony_ci policy->cpuinfo.transition_latency = 2578c2ecf20Sopenharmony_ci data->acpi_data.states[i].transition_latency * 1000; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* table init */ 2628c2ecf20Sopenharmony_ci for (i = 0; i <= data->acpi_data.state_count; i++) 2638c2ecf20Sopenharmony_ci { 2648c2ecf20Sopenharmony_ci if (i < data->acpi_data.state_count) { 2658c2ecf20Sopenharmony_ci freq_table[i].frequency = 2668c2ecf20Sopenharmony_ci data->acpi_data.states[i].core_frequency * 1000; 2678c2ecf20Sopenharmony_ci } else { 2688c2ecf20Sopenharmony_ci freq_table[i].frequency = CPUFREQ_TABLE_END; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci policy->freq_table = freq_table; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* notify BIOS that we exist */ 2758c2ecf20Sopenharmony_ci acpi_processor_notify_smm(THIS_MODULE); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci pr_info("CPU%u - ACPI performance management activated\n", cpu); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci for (i = 0; i < data->acpi_data.state_count; i++) 2808c2ecf20Sopenharmony_ci pr_debug(" %cP%d: %d MHz, %d mW, %d uS, %d uS, 0x%x 0x%x\n", 2818c2ecf20Sopenharmony_ci (i == data->acpi_data.state?'*':' '), i, 2828c2ecf20Sopenharmony_ci (u32) data->acpi_data.states[i].core_frequency, 2838c2ecf20Sopenharmony_ci (u32) data->acpi_data.states[i].power, 2848c2ecf20Sopenharmony_ci (u32) data->acpi_data.states[i].transition_latency, 2858c2ecf20Sopenharmony_ci (u32) data->acpi_data.states[i].bus_master_latency, 2868c2ecf20Sopenharmony_ci (u32) data->acpi_data.states[i].status, 2878c2ecf20Sopenharmony_ci (u32) data->acpi_data.states[i].control); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* the first call to ->target() should result in us actually 2908c2ecf20Sopenharmony_ci * writing something to the appropriate registers. */ 2918c2ecf20Sopenharmony_ci data->resume = 1; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return (result); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci err_unreg: 2968c2ecf20Sopenharmony_ci acpi_processor_unregister_performance(cpu); 2978c2ecf20Sopenharmony_ci err_free: 2988c2ecf20Sopenharmony_ci kfree(data); 2998c2ecf20Sopenharmony_ci acpi_io_data[cpu] = NULL; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return (result); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int 3068c2ecf20Sopenharmony_ciacpi_cpufreq_cpu_exit ( 3078c2ecf20Sopenharmony_ci struct cpufreq_policy *policy) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci pr_debug("acpi_cpufreq_cpu_exit\n"); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (data) { 3148c2ecf20Sopenharmony_ci acpi_io_data[policy->cpu] = NULL; 3158c2ecf20Sopenharmony_ci acpi_processor_unregister_performance(policy->cpu); 3168c2ecf20Sopenharmony_ci kfree(policy->freq_table); 3178c2ecf20Sopenharmony_ci kfree(data); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return (0); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic struct cpufreq_driver acpi_cpufreq_driver = { 3258c2ecf20Sopenharmony_ci .verify = cpufreq_generic_frequency_table_verify, 3268c2ecf20Sopenharmony_ci .target_index = acpi_cpufreq_target, 3278c2ecf20Sopenharmony_ci .get = acpi_cpufreq_get, 3288c2ecf20Sopenharmony_ci .init = acpi_cpufreq_cpu_init, 3298c2ecf20Sopenharmony_ci .exit = acpi_cpufreq_cpu_exit, 3308c2ecf20Sopenharmony_ci .name = "acpi-cpufreq", 3318c2ecf20Sopenharmony_ci .attr = cpufreq_generic_attr, 3328c2ecf20Sopenharmony_ci}; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int __init 3368c2ecf20Sopenharmony_ciacpi_cpufreq_init (void) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci pr_debug("acpi_cpufreq_init\n"); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return cpufreq_register_driver(&acpi_cpufreq_driver); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic void __exit 3458c2ecf20Sopenharmony_ciacpi_cpufreq_exit (void) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci pr_debug("acpi_cpufreq_exit\n"); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci cpufreq_unregister_driver(&acpi_cpufreq_driver); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cilate_initcall(acpi_cpufreq_init); 3538c2ecf20Sopenharmony_cimodule_exit(acpi_cpufreq_exit); 354