162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2005 Intel Corporation 462306a36Sopenharmony_ci * Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> 562306a36Sopenharmony_ci * - Added _PDC for SMP C-states on Intel CPUs 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/acpi.h> 1262306a36Sopenharmony_ci#include <linux/cpu.h> 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <acpi/processor.h> 1662306a36Sopenharmony_ci#include <asm/mwait.h> 1762306a36Sopenharmony_ci#include <asm/special_insns.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * Initialize bm_flags based on the CPU cache properties 2162306a36Sopenharmony_ci * On SMP it depends on cache configuration 2262306a36Sopenharmony_ci * - When cache is not shared among all CPUs, we flush cache 2362306a36Sopenharmony_ci * before entering C3. 2462306a36Sopenharmony_ci * - When cache is shared among all CPUs, we use bm_check 2562306a36Sopenharmony_ci * mechanism as in UP case 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * This routine is called only after all the CPUs are online 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_civoid acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags, 3062306a36Sopenharmony_ci unsigned int cpu) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct cpuinfo_x86 *c = &cpu_data(cpu); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci flags->bm_check = 0; 3562306a36Sopenharmony_ci if (num_online_cpus() == 1) 3662306a36Sopenharmony_ci flags->bm_check = 1; 3762306a36Sopenharmony_ci else if (c->x86_vendor == X86_VENDOR_INTEL) { 3862306a36Sopenharmony_ci /* 3962306a36Sopenharmony_ci * Today all MP CPUs that support C3 share cache. 4062306a36Sopenharmony_ci * And caches should not be flushed by software while 4162306a36Sopenharmony_ci * entering C3 type state. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci flags->bm_check = 1; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* 4762306a36Sopenharmony_ci * On all recent Intel platforms, ARB_DISABLE is a nop. 4862306a36Sopenharmony_ci * So, set bm_control to zero to indicate that ARB_DISABLE 4962306a36Sopenharmony_ci * is not required while entering C3 type state on 5062306a36Sopenharmony_ci * P4, Core and beyond CPUs 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci if (c->x86_vendor == X86_VENDOR_INTEL && 5362306a36Sopenharmony_ci (c->x86 > 0xf || (c->x86 == 6 && c->x86_model >= 0x0f))) 5462306a36Sopenharmony_ci flags->bm_control = 0; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (c->x86_vendor == X86_VENDOR_CENTAUR) { 5762306a36Sopenharmony_ci if (c->x86 > 6 || (c->x86 == 6 && c->x86_model == 0x0f && 5862306a36Sopenharmony_ci c->x86_stepping >= 0x0e)) { 5962306a36Sopenharmony_ci /* 6062306a36Sopenharmony_ci * For all recent Centaur CPUs, the ucode will make sure that each 6162306a36Sopenharmony_ci * core can keep cache coherence with each other while entering C3 6262306a36Sopenharmony_ci * type state. So, set bm_check to 1 to indicate that the kernel 6362306a36Sopenharmony_ci * doesn't need to execute a cache flush operation (WBINVD) when 6462306a36Sopenharmony_ci * entering C3 type state. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci flags->bm_check = 1; 6762306a36Sopenharmony_ci /* 6862306a36Sopenharmony_ci * For all recent Centaur platforms, ARB_DISABLE is a nop. 6962306a36Sopenharmony_ci * Set bm_control to zero to indicate that ARB_DISABLE is 7062306a36Sopenharmony_ci * not required while entering C3 type state. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci flags->bm_control = 0; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (c->x86_vendor == X86_VENDOR_ZHAOXIN) { 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * All Zhaoxin CPUs that support C3 share cache. 7962306a36Sopenharmony_ci * And caches should not be flushed by software while 8062306a36Sopenharmony_ci * entering C3 type state. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci flags->bm_check = 1; 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * On all recent Zhaoxin platforms, ARB_DISABLE is a nop. 8562306a36Sopenharmony_ci * So, set bm_control to zero to indicate that ARB_DISABLE 8662306a36Sopenharmony_ci * is not required while entering C3 type state. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci flags->bm_control = 0; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci if (c->x86_vendor == X86_VENDOR_AMD && c->x86 >= 0x17) { 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * For all AMD Zen or newer CPUs that support C3, caches 9362306a36Sopenharmony_ci * should not be flushed by software while entering C3 9462306a36Sopenharmony_ci * type state. Set bm->check to 1 so that kernel doesn't 9562306a36Sopenharmony_ci * need to execute cache flush operation. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci flags->bm_check = 1; 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * In current AMD C state implementation ARB_DIS is no longer 10062306a36Sopenharmony_ci * used. So set bm_control to zero to indicate ARB_DIS is not 10162306a36Sopenharmony_ci * required while entering C3 type state. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci flags->bm_control = 0; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ciEXPORT_SYMBOL(acpi_processor_power_init_bm_check); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* The code below handles cstate entry with monitor-mwait pair on Intel*/ 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct cstate_entry { 11162306a36Sopenharmony_ci struct { 11262306a36Sopenharmony_ci unsigned int eax; 11362306a36Sopenharmony_ci unsigned int ecx; 11462306a36Sopenharmony_ci } states[ACPI_PROCESSOR_MAX_POWER]; 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_cistatic struct cstate_entry __percpu *cpu_cstate_entry; /* per CPU ptr */ 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic short mwait_supported[ACPI_PROCESSOR_MAX_POWER]; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define NATIVE_CSTATE_BEYOND_HALT (2) 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic long acpi_processor_ffh_cstate_probe_cpu(void *_cx) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct acpi_processor_cx *cx = _cx; 12562306a36Sopenharmony_ci long retval; 12662306a36Sopenharmony_ci unsigned int eax, ebx, ecx, edx; 12762306a36Sopenharmony_ci unsigned int edx_part; 12862306a36Sopenharmony_ci unsigned int cstate_type; /* C-state type and not ACPI C-state type */ 12962306a36Sopenharmony_ci unsigned int num_cstate_subtype; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Check whether this particular cx_type (in CST) is supported or not */ 13462306a36Sopenharmony_ci cstate_type = ((cx->address >> MWAIT_SUBSTATE_SIZE) & 13562306a36Sopenharmony_ci MWAIT_CSTATE_MASK) + 1; 13662306a36Sopenharmony_ci edx_part = edx >> (cstate_type * MWAIT_SUBSTATE_SIZE); 13762306a36Sopenharmony_ci num_cstate_subtype = edx_part & MWAIT_SUBSTATE_MASK; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci retval = 0; 14062306a36Sopenharmony_ci /* If the HW does not support any sub-states in this C-state */ 14162306a36Sopenharmony_ci if (num_cstate_subtype == 0) { 14262306a36Sopenharmony_ci pr_warn(FW_BUG "ACPI MWAIT C-state 0x%x not supported by HW (0x%x)\n", 14362306a36Sopenharmony_ci cx->address, edx_part); 14462306a36Sopenharmony_ci retval = -1; 14562306a36Sopenharmony_ci goto out; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* mwait ecx extensions INTERRUPT_BREAK should be supported for C2/C3 */ 14962306a36Sopenharmony_ci if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || 15062306a36Sopenharmony_ci !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) { 15162306a36Sopenharmony_ci retval = -1; 15262306a36Sopenharmony_ci goto out; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (!mwait_supported[cstate_type]) { 15662306a36Sopenharmony_ci mwait_supported[cstate_type] = 1; 15762306a36Sopenharmony_ci printk(KERN_DEBUG 15862306a36Sopenharmony_ci "Monitor-Mwait will be used to enter C-%d state\n", 15962306a36Sopenharmony_ci cx->type); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci snprintf(cx->desc, 16262306a36Sopenharmony_ci ACPI_CX_DESC_LEN, "ACPI FFH MWAIT 0x%x", 16362306a36Sopenharmony_ci cx->address); 16462306a36Sopenharmony_ciout: 16562306a36Sopenharmony_ci return retval; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciint acpi_processor_ffh_cstate_probe(unsigned int cpu, 16962306a36Sopenharmony_ci struct acpi_processor_cx *cx, struct acpi_power_register *reg) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct cstate_entry *percpu_entry; 17262306a36Sopenharmony_ci struct cpuinfo_x86 *c = &cpu_data(cpu); 17362306a36Sopenharmony_ci long retval; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (!cpu_cstate_entry || c->cpuid_level < CPUID_MWAIT_LEAF) 17662306a36Sopenharmony_ci return -1; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (reg->bit_offset != NATIVE_CSTATE_BEYOND_HALT) 17962306a36Sopenharmony_ci return -1; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu); 18262306a36Sopenharmony_ci percpu_entry->states[cx->index].eax = 0; 18362306a36Sopenharmony_ci percpu_entry->states[cx->index].ecx = 0; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* Make sure we are running on right CPU */ 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci retval = call_on_cpu(cpu, acpi_processor_ffh_cstate_probe_cpu, cx, 18862306a36Sopenharmony_ci false); 18962306a36Sopenharmony_ci if (retval == 0) { 19062306a36Sopenharmony_ci /* Use the hint in CST */ 19162306a36Sopenharmony_ci percpu_entry->states[cx->index].eax = cx->address; 19262306a36Sopenharmony_ci percpu_entry->states[cx->index].ecx = MWAIT_ECX_INTERRUPT_BREAK; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * For _CST FFH on Intel, if GAS.access_size bit 1 is cleared, 19762306a36Sopenharmony_ci * then we should skip checking BM_STS for this C-state. 19862306a36Sopenharmony_ci * ref: "Intel Processor Vendor-Specific ACPI Interface Specification" 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci if ((c->x86_vendor == X86_VENDOR_INTEL) && !(reg->access_size & 0x2)) 20162306a36Sopenharmony_ci cx->bm_sts_skip = 1; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return retval; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_probe); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_civoid __cpuidle acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci unsigned int cpu = smp_processor_id(); 21062306a36Sopenharmony_ci struct cstate_entry *percpu_entry; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu); 21362306a36Sopenharmony_ci mwait_idle_with_hints(percpu_entry->states[cx->index].eax, 21462306a36Sopenharmony_ci percpu_entry->states[cx->index].ecx); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_enter); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int __init ffh_cstate_init(void) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct cpuinfo_x86 *c = &boot_cpu_data; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (c->x86_vendor != X86_VENDOR_INTEL && 22362306a36Sopenharmony_ci c->x86_vendor != X86_VENDOR_AMD && 22462306a36Sopenharmony_ci c->x86_vendor != X86_VENDOR_HYGON) 22562306a36Sopenharmony_ci return -1; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci cpu_cstate_entry = alloc_percpu(struct cstate_entry); 22862306a36Sopenharmony_ci return 0; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void __exit ffh_cstate_exit(void) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci free_percpu(cpu_cstate_entry); 23462306a36Sopenharmony_ci cpu_cstate_entry = NULL; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ciarch_initcall(ffh_cstate_init); 23862306a36Sopenharmony_ci__exitcall(ffh_cstate_exit); 239