18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Pentium 4/Xeon CPU on demand clock modulation/speed scaling 48c2ecf20Sopenharmony_ci * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> 58c2ecf20Sopenharmony_ci * (C) 2002 Zwane Mwaikambo <zwane@commfireservices.com> 68c2ecf20Sopenharmony_ci * (C) 2002 Arjan van de Ven <arjanv@redhat.com> 78c2ecf20Sopenharmony_ci * (C) 2002 Tora T. Engstad 88c2ecf20Sopenharmony_ci * All Rights Reserved 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * The author(s) of this software shall not be held liable for damages 118c2ecf20Sopenharmony_ci * of any nature resulting due to the use of this software. This 128c2ecf20Sopenharmony_ci * software is provided AS-IS with no warranties. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Date Errata Description 158c2ecf20Sopenharmony_ci * 20020525 N44, O17 12.5% or 25% DC causes lockup 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/init.h> 238c2ecf20Sopenharmony_ci#include <linux/smp.h> 248c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 258c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 268c2ecf20Sopenharmony_ci#include <linux/timex.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <asm/processor.h> 298c2ecf20Sopenharmony_ci#include <asm/msr.h> 308c2ecf20Sopenharmony_ci#include <asm/timer.h> 318c2ecf20Sopenharmony_ci#include <asm/cpu_device_id.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include "speedstep-lib.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * Duty Cycle (3bits), note DC_DISABLE is not specified in 378c2ecf20Sopenharmony_ci * intel docs i just use it to mean disable 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cienum { 408c2ecf20Sopenharmony_ci DC_RESV, DC_DFLT, DC_25PT, DC_38PT, DC_50PT, 418c2ecf20Sopenharmony_ci DC_64PT, DC_75PT, DC_88PT, DC_DISABLE 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define DC_ENTRIES 8 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int has_N44_O17_errata[NR_CPUS]; 488c2ecf20Sopenharmony_cistatic unsigned int stock_freq; 498c2ecf20Sopenharmony_cistatic struct cpufreq_driver p4clockmod_driver; 508c2ecf20Sopenharmony_cistatic unsigned int cpufreq_p4_get(unsigned int cpu); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci u32 l, h; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if ((newstate > DC_DISABLE) || (newstate == DC_RESV)) 578c2ecf20Sopenharmony_ci return -EINVAL; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &l, &h); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (l & 0x01) 628c2ecf20Sopenharmony_ci pr_debug("CPU#%d currently thermal throttled\n", cpu); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (has_N44_O17_errata[cpu] && 658c2ecf20Sopenharmony_ci (newstate == DC_25PT || newstate == DC_DFLT)) 668c2ecf20Sopenharmony_ci newstate = DC_38PT; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h); 698c2ecf20Sopenharmony_ci if (newstate == DC_DISABLE) { 708c2ecf20Sopenharmony_ci pr_debug("CPU#%d disabling modulation\n", cpu); 718c2ecf20Sopenharmony_ci wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l & ~(1<<4), h); 728c2ecf20Sopenharmony_ci } else { 738c2ecf20Sopenharmony_ci pr_debug("CPU#%d setting duty cycle to %d%%\n", 748c2ecf20Sopenharmony_ci cpu, ((125 * newstate) / 10)); 758c2ecf20Sopenharmony_ci /* bits 63 - 5 : reserved 768c2ecf20Sopenharmony_ci * bit 4 : enable/disable 778c2ecf20Sopenharmony_ci * bits 3-1 : duty cycle 788c2ecf20Sopenharmony_ci * bit 0 : reserved 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci l = (l & ~14); 818c2ecf20Sopenharmony_ci l = l | (1<<4) | ((newstate & 0x7)<<1); 828c2ecf20Sopenharmony_ci wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l, h); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic struct cpufreq_frequency_table p4clockmod_table[] = { 908c2ecf20Sopenharmony_ci {0, DC_RESV, CPUFREQ_ENTRY_INVALID}, 918c2ecf20Sopenharmony_ci {0, DC_DFLT, 0}, 928c2ecf20Sopenharmony_ci {0, DC_25PT, 0}, 938c2ecf20Sopenharmony_ci {0, DC_38PT, 0}, 948c2ecf20Sopenharmony_ci {0, DC_50PT, 0}, 958c2ecf20Sopenharmony_ci {0, DC_64PT, 0}, 968c2ecf20Sopenharmony_ci {0, DC_75PT, 0}, 978c2ecf20Sopenharmony_ci {0, DC_88PT, 0}, 988c2ecf20Sopenharmony_ci {0, DC_DISABLE, 0}, 998c2ecf20Sopenharmony_ci {0, DC_RESV, CPUFREQ_TABLE_END}, 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int cpufreq_p4_target(struct cpufreq_policy *policy, unsigned int index) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci int i; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* run on each logical CPU, 1088c2ecf20Sopenharmony_ci * see section 13.15.3 of IA32 Intel Architecture Software 1098c2ecf20Sopenharmony_ci * Developer's Manual, Volume 3 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci for_each_cpu(i, policy->cpus) 1128c2ecf20Sopenharmony_ci cpufreq_p4_setdc(i, p4clockmod_table[index].driver_data); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci if (c->x86 == 0x06) { 1218c2ecf20Sopenharmony_ci if (cpu_has(c, X86_FEATURE_EST)) 1228c2ecf20Sopenharmony_ci pr_warn_once("Warning: EST-capable CPU detected. The acpi-cpufreq module offers voltage scaling in addition to frequency scaling. You should use that instead of p4-clockmod, if possible.\n"); 1238c2ecf20Sopenharmony_ci switch (c->x86_model) { 1248c2ecf20Sopenharmony_ci case 0x0E: /* Core */ 1258c2ecf20Sopenharmony_ci case 0x0F: /* Core Duo */ 1268c2ecf20Sopenharmony_ci case 0x16: /* Celeron Core */ 1278c2ecf20Sopenharmony_ci case 0x1C: /* Atom */ 1288c2ecf20Sopenharmony_ci p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; 1298c2ecf20Sopenharmony_ci return speedstep_get_frequency(SPEEDSTEP_CPU_PCORE); 1308c2ecf20Sopenharmony_ci case 0x0D: /* Pentium M (Dothan) */ 1318c2ecf20Sopenharmony_ci p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; 1328c2ecf20Sopenharmony_ci fallthrough; 1338c2ecf20Sopenharmony_ci case 0x09: /* Pentium M (Banias) */ 1348c2ecf20Sopenharmony_ci return speedstep_get_frequency(SPEEDSTEP_CPU_PM); 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (c->x86 != 0xF) 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* on P-4s, the TSC runs with constant frequency independent whether 1428c2ecf20Sopenharmony_ci * throttling is active or not. */ 1438c2ecf20Sopenharmony_ci p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) { 1468c2ecf20Sopenharmony_ci pr_warn("Warning: Pentium 4-M detected. The speedstep-ich or acpi cpufreq modules offer voltage scaling in addition of frequency scaling. You should use either one instead of p4-clockmod, if possible.\n"); 1478c2ecf20Sopenharmony_ci return speedstep_get_frequency(SPEEDSTEP_CPU_P4M); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return speedstep_get_frequency(SPEEDSTEP_CPU_P4D); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int cpufreq_p4_cpu_init(struct cpufreq_policy *policy) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct cpuinfo_x86 *c = &cpu_data(policy->cpu); 1588c2ecf20Sopenharmony_ci int cpuid = 0; 1598c2ecf20Sopenharmony_ci unsigned int i; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 1628c2ecf20Sopenharmony_ci cpumask_copy(policy->cpus, topology_sibling_cpumask(policy->cpu)); 1638c2ecf20Sopenharmony_ci#endif 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Errata workaround */ 1668c2ecf20Sopenharmony_ci cpuid = (c->x86 << 8) | (c->x86_model << 4) | c->x86_stepping; 1678c2ecf20Sopenharmony_ci switch (cpuid) { 1688c2ecf20Sopenharmony_ci case 0x0f07: 1698c2ecf20Sopenharmony_ci case 0x0f0a: 1708c2ecf20Sopenharmony_ci case 0x0f11: 1718c2ecf20Sopenharmony_ci case 0x0f12: 1728c2ecf20Sopenharmony_ci has_N44_O17_errata[policy->cpu] = 1; 1738c2ecf20Sopenharmony_ci pr_debug("has errata -- disabling low frequencies\n"); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4D && 1778c2ecf20Sopenharmony_ci c->x86_model < 2) { 1788c2ecf20Sopenharmony_ci /* switch to maximum frequency and measure result */ 1798c2ecf20Sopenharmony_ci cpufreq_p4_setdc(policy->cpu, DC_DISABLE); 1808c2ecf20Sopenharmony_ci recalibrate_cpu_khz(); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci /* get max frequency */ 1838c2ecf20Sopenharmony_ci stock_freq = cpufreq_p4_get_frequency(c); 1848c2ecf20Sopenharmony_ci if (!stock_freq) 1858c2ecf20Sopenharmony_ci return -EINVAL; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* table init */ 1888c2ecf20Sopenharmony_ci for (i = 1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) { 1898c2ecf20Sopenharmony_ci if ((i < 2) && (has_N44_O17_errata[policy->cpu])) 1908c2ecf20Sopenharmony_ci p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID; 1918c2ecf20Sopenharmony_ci else 1928c2ecf20Sopenharmony_ci p4clockmod_table[i].frequency = (stock_freq * i)/8; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* cpuinfo and default policy values */ 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* the transition latency is set to be 1 higher than the maximum 1988c2ecf20Sopenharmony_ci * transition latency of the ondemand governor */ 1998c2ecf20Sopenharmony_ci policy->cpuinfo.transition_latency = 10000001; 2008c2ecf20Sopenharmony_ci policy->freq_table = &p4clockmod_table[0]; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic unsigned int cpufreq_p4_get(unsigned int cpu) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci u32 l, h; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (l & 0x10) { 2138c2ecf20Sopenharmony_ci l = l >> 1; 2148c2ecf20Sopenharmony_ci l &= 0x7; 2158c2ecf20Sopenharmony_ci } else 2168c2ecf20Sopenharmony_ci l = DC_DISABLE; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (l != DC_DISABLE) 2198c2ecf20Sopenharmony_ci return stock_freq * l / 8; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return stock_freq; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic struct cpufreq_driver p4clockmod_driver = { 2258c2ecf20Sopenharmony_ci .verify = cpufreq_generic_frequency_table_verify, 2268c2ecf20Sopenharmony_ci .target_index = cpufreq_p4_target, 2278c2ecf20Sopenharmony_ci .init = cpufreq_p4_cpu_init, 2288c2ecf20Sopenharmony_ci .get = cpufreq_p4_get, 2298c2ecf20Sopenharmony_ci .name = "p4-clockmod", 2308c2ecf20Sopenharmony_ci .attr = cpufreq_generic_attr, 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic const struct x86_cpu_id cpufreq_p4_id[] = { 2348c2ecf20Sopenharmony_ci X86_MATCH_VENDOR_FEATURE(INTEL, X86_FEATURE_ACC, NULL), 2358c2ecf20Sopenharmony_ci {} 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* 2398c2ecf20Sopenharmony_ci * Intentionally no MODULE_DEVICE_TABLE here: this driver should not 2408c2ecf20Sopenharmony_ci * be auto loaded. Please don't add one. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic int __init cpufreq_p4_init(void) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci int ret; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* 2488c2ecf20Sopenharmony_ci * THERM_CONTROL is architectural for IA32 now, so 2498c2ecf20Sopenharmony_ci * we can rely on the capability checks 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci if (!x86_match_cpu(cpufreq_p4_id) || !boot_cpu_has(X86_FEATURE_ACPI)) 2528c2ecf20Sopenharmony_ci return -ENODEV; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ret = cpufreq_register_driver(&p4clockmod_driver); 2558c2ecf20Sopenharmony_ci if (!ret) 2568c2ecf20Sopenharmony_ci pr_info("P4/Xeon(TM) CPU On-Demand Clock Modulation available\n"); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return ret; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic void __exit cpufreq_p4_exit(void) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci cpufreq_unregister_driver(&p4clockmod_driver); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>"); 2698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("cpufreq driver for Pentium(TM) 4/Xeon(TM)"); 2708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cilate_initcall(cpufreq_p4_init); 2738c2ecf20Sopenharmony_cimodule_exit(cpufreq_p4_exit); 274