162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef _ASM_X86_MWAIT_H
362306a36Sopenharmony_ci#define _ASM_X86_MWAIT_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/sched.h>
662306a36Sopenharmony_ci#include <linux/sched/idle.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <asm/cpufeature.h>
962306a36Sopenharmony_ci#include <asm/nospec-branch.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define MWAIT_SUBSTATE_MASK		0xf
1262306a36Sopenharmony_ci#define MWAIT_CSTATE_MASK		0xf
1362306a36Sopenharmony_ci#define MWAIT_SUBSTATE_SIZE		4
1462306a36Sopenharmony_ci#define MWAIT_HINT2CSTATE(hint)		(((hint) >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK)
1562306a36Sopenharmony_ci#define MWAIT_HINT2SUBSTATE(hint)	((hint) & MWAIT_CSTATE_MASK)
1662306a36Sopenharmony_ci#define MWAIT_C1_SUBSTATE_MASK  0xf0
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define CPUID_MWAIT_LEAF		5
1962306a36Sopenharmony_ci#define CPUID5_ECX_EXTENSIONS_SUPPORTED 0x1
2062306a36Sopenharmony_ci#define CPUID5_ECX_INTERRUPT_BREAK	0x2
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define MWAIT_ECX_INTERRUPT_BREAK	0x1
2362306a36Sopenharmony_ci#define MWAITX_ECX_TIMER_ENABLE		BIT(1)
2462306a36Sopenharmony_ci#define MWAITX_MAX_WAIT_CYCLES		UINT_MAX
2562306a36Sopenharmony_ci#define MWAITX_DISABLE_CSTATES		0xf0
2662306a36Sopenharmony_ci#define TPAUSE_C01_STATE		1
2762306a36Sopenharmony_ci#define TPAUSE_C02_STATE		0
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic __always_inline void __monitor(const void *eax, unsigned long ecx,
3062306a36Sopenharmony_ci			     unsigned long edx)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	/* "monitor %eax, %ecx, %edx;" */
3362306a36Sopenharmony_ci	asm volatile(".byte 0x0f, 0x01, 0xc8;"
3462306a36Sopenharmony_ci		     :: "a" (eax), "c" (ecx), "d"(edx));
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic __always_inline void __monitorx(const void *eax, unsigned long ecx,
3862306a36Sopenharmony_ci			      unsigned long edx)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	/* "monitorx %eax, %ecx, %edx;" */
4162306a36Sopenharmony_ci	asm volatile(".byte 0x0f, 0x01, 0xfa;"
4262306a36Sopenharmony_ci		     :: "a" (eax), "c" (ecx), "d"(edx));
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic __always_inline void __mwait(unsigned long eax, unsigned long ecx)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	mds_idle_clear_cpu_buffers();
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* "mwait %eax, %ecx;" */
5062306a36Sopenharmony_ci	asm volatile(".byte 0x0f, 0x01, 0xc9;"
5162306a36Sopenharmony_ci		     :: "a" (eax), "c" (ecx));
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/*
5562306a36Sopenharmony_ci * MWAITX allows for a timer expiration to get the core out a wait state in
5662306a36Sopenharmony_ci * addition to the default MWAIT exit condition of a store appearing at a
5762306a36Sopenharmony_ci * monitored virtual address.
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci * Registers:
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * MWAITX ECX[1]: enable timer if set
6262306a36Sopenharmony_ci * MWAITX EBX[31:0]: max wait time expressed in SW P0 clocks. The software P0
6362306a36Sopenharmony_ci * frequency is the same as the TSC frequency.
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * Below is a comparison between MWAIT and MWAITX on AMD processors:
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci *                 MWAIT                           MWAITX
6862306a36Sopenharmony_ci * opcode          0f 01 c9           |            0f 01 fb
6962306a36Sopenharmony_ci * ECX[0]                  value of RFLAGS.IF seen by instruction
7062306a36Sopenharmony_ci * ECX[1]          unused/#GP if set  |            enable timer if set
7162306a36Sopenharmony_ci * ECX[31:2]                     unused/#GP if set
7262306a36Sopenharmony_ci * EAX                           unused (reserve for hint)
7362306a36Sopenharmony_ci * EBX[31:0]       unused             |            max wait time (P0 clocks)
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci *                 MONITOR                         MONITORX
7662306a36Sopenharmony_ci * opcode          0f 01 c8           |            0f 01 fa
7762306a36Sopenharmony_ci * EAX                     (logical) address to monitor
7862306a36Sopenharmony_ci * ECX                     #GP if not zero
7962306a36Sopenharmony_ci */
8062306a36Sopenharmony_cistatic __always_inline void __mwaitx(unsigned long eax, unsigned long ebx,
8162306a36Sopenharmony_ci				     unsigned long ecx)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	/* No MDS buffer clear as this is AMD/HYGON only */
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* "mwaitx %eax, %ebx, %ecx;" */
8662306a36Sopenharmony_ci	asm volatile(".byte 0x0f, 0x01, 0xfb;"
8762306a36Sopenharmony_ci		     :: "a" (eax), "b" (ebx), "c" (ecx));
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic __always_inline void __sti_mwait(unsigned long eax, unsigned long ecx)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	mds_idle_clear_cpu_buffers();
9362306a36Sopenharmony_ci	/* "mwait %eax, %ecx;" */
9462306a36Sopenharmony_ci	asm volatile("sti; .byte 0x0f, 0x01, 0xc9;"
9562306a36Sopenharmony_ci		     :: "a" (eax), "c" (ecx));
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/*
9962306a36Sopenharmony_ci * This uses new MONITOR/MWAIT instructions on P4 processors with PNI,
10062306a36Sopenharmony_ci * which can obviate IPI to trigger checking of need_resched.
10162306a36Sopenharmony_ci * We execute MONITOR against need_resched and enter optimized wait state
10262306a36Sopenharmony_ci * through MWAIT. Whenever someone changes need_resched, we would be woken
10362306a36Sopenharmony_ci * up from MWAIT (without an IPI).
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci * New with Core Duo processors, MWAIT can take some hints based on CPU
10662306a36Sopenharmony_ci * capability.
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_cistatic __always_inline void mwait_idle_with_hints(unsigned long eax, unsigned long ecx)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	if (static_cpu_has_bug(X86_BUG_MONITOR) || !current_set_polling_and_test()) {
11162306a36Sopenharmony_ci		if (static_cpu_has_bug(X86_BUG_CLFLUSH_MONITOR)) {
11262306a36Sopenharmony_ci			mb();
11362306a36Sopenharmony_ci			clflush((void *)&current_thread_info()->flags);
11462306a36Sopenharmony_ci			mb();
11562306a36Sopenharmony_ci		}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		__monitor((void *)&current_thread_info()->flags, 0, 0);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		if (!need_resched()) {
12062306a36Sopenharmony_ci			if (ecx & 1) {
12162306a36Sopenharmony_ci				__mwait(eax, ecx);
12262306a36Sopenharmony_ci			} else {
12362306a36Sopenharmony_ci				__sti_mwait(eax, ecx);
12462306a36Sopenharmony_ci				raw_local_irq_disable();
12562306a36Sopenharmony_ci			}
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci	current_clr_polling();
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/*
13262306a36Sopenharmony_ci * Caller can specify whether to enter C0.1 (low latency, less
13362306a36Sopenharmony_ci * power saving) or C0.2 state (saves more power, but longer wakeup
13462306a36Sopenharmony_ci * latency). This may be overridden by the IA32_UMWAIT_CONTROL MSR
13562306a36Sopenharmony_ci * which can force requests for C0.2 to be downgraded to C0.1.
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_cistatic inline void __tpause(u32 ecx, u32 edx, u32 eax)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	/* "tpause %ecx, %edx, %eax;" */
14062306a36Sopenharmony_ci	#ifdef CONFIG_AS_TPAUSE
14162306a36Sopenharmony_ci	asm volatile("tpause %%ecx\n"
14262306a36Sopenharmony_ci		     :
14362306a36Sopenharmony_ci		     : "c"(ecx), "d"(edx), "a"(eax));
14462306a36Sopenharmony_ci	#else
14562306a36Sopenharmony_ci	asm volatile(".byte 0x66, 0x0f, 0xae, 0xf1\t\n"
14662306a36Sopenharmony_ci		     :
14762306a36Sopenharmony_ci		     : "c"(ecx), "d"(edx), "a"(eax));
14862306a36Sopenharmony_ci	#endif
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci#endif /* _ASM_X86_MWAIT_H */
152