1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * This file is subject to the terms and conditions of the GNU General Public
4 * License.  See the file "COPYING" in the main directory of this archive
5 * for more details.
6 *
7 * Copyright (C) 2020 Loongson Technology Corporation Limited
8 *
9 */
10#ifndef _ASM_LBT_H
11#define _ASM_LBT_H
12
13#include <asm/asm.h>
14#include <asm/asm-offsets.h>
15#include <asm/loongarchregs.h>
16
17#ifdef CONFIG_CPU_HAS_LBT
18
19#define STR(x)  __STR(x)
20#define __STR(x)  #x
21
22extern void _init_lbt(void);
23
24static inline void save_lbt_registers(struct loongarch_lbt *prev)
25{
26	unsigned long tmp = 0;
27
28	__asm__ __volatile__ (
29	"movscr2gr  %[tmp], $scr0					\n"
30	"stptr.d %[tmp], %[prev], " STR(THREAD_SCR0) "		\n"
31	"movscr2gr  %[tmp], $scr1					\n"
32	"stptr.d %[tmp], %[prev], " STR(THREAD_SCR1) "		\n"
33	"movscr2gr  %[tmp], $scr2					\n"
34	"stptr.d %[tmp], %[prev], " STR(THREAD_SCR2) "		\n"
35	"movscr2gr  %[tmp], $scr3					\n"
36	"stptr.d %[tmp], %[prev], " STR(THREAD_SCR3) "		\n"
37	"x86mfflag %[tmp], 0x3f					\n"
38	"stptr.d %[tmp], %[prev], " STR(THREAD_EFLAGS) "	\n"
39	:
40	: [prev] "r" (prev), [tmp] "r" (tmp)
41	: "memory"
42	);
43}
44
45static inline void restore_lbt_registers(struct loongarch_lbt *next)
46{
47	unsigned long tmp = 0;
48
49	__asm__ __volatile__ (
50	"ldptr.d %[tmp], %[next], " STR(THREAD_SCR0) "		\n"
51	"movgr2scr  $scr0, %[tmp]					\n"
52	"ldptr.d %[tmp], %[next], " STR(THREAD_SCR1) "		\n"
53	"movgr2scr  $scr1, %[tmp]					\n"
54	"ldptr.d %[tmp], %[next], " STR(THREAD_SCR2) "		\n"
55	"movgr2scr  $scr2, %[tmp]					\n"
56	"ldptr.d %[tmp], %[next], " STR(THREAD_SCR3) "		\n"
57	"movgr2scr  $scr3, %[tmp]					\n"
58	"ldptr.d %[tmp], %[next], " STR(THREAD_EFLAGS) "	\n"
59	"x86mtflag %[tmp], 0x3f					\n"
60	:
61	: [next] "r" (next), [tmp] "r" (tmp)
62	:
63	);
64}
65
66static inline void enable_lbt(void)
67{
68	if (cpu_has_lbt)
69		csr_xchg32(CSR_EUEN_LBTEN, CSR_EUEN_LBTEN, LOONGARCH_CSR_EUEN);
70}
71
72static inline void disable_lbt(void)
73{
74	if (cpu_has_lbt)
75		csr_xchg32(0, CSR_EUEN_LBTEN, LOONGARCH_CSR_EUEN);
76}
77
78static inline int is_lbt_enabled(void)
79{
80	if (!cpu_has_lbt)
81		return 0;
82
83	return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_LBTEN) ?
84		1 : 0;
85}
86
87static inline int is_lbt_owner(void)
88{
89	return test_thread_flag(TIF_USEDLBT);
90}
91
92static inline void __own_lbt(void)
93{
94	enable_lbt();
95	set_thread_flag(TIF_USEDLBT);
96	KSTK_EUEN(current) |= CSR_EUEN_LBTEN;
97}
98
99static inline void init_lbt(void)
100{
101	__own_lbt();
102	_init_lbt();
103}
104
105static inline void own_lbt_inatomic(int restore)
106{
107	if (cpu_has_lbt && !is_lbt_owner()) {
108		__own_lbt();
109		if (restore)
110			restore_lbt_registers(&current->thread.lbt);
111	}
112}
113
114static inline void lose_lbt_inatomic(int save, struct task_struct *tsk)
115{
116	if (is_lbt_owner()) {
117		if (save)
118			save_lbt_registers(&tsk->thread.lbt);
119
120		disable_lbt();
121		clear_tsk_thread_flag(tsk, TIF_USEDLBT);
122	}
123	KSTK_EUEN(tsk) &= ~(CSR_EUEN_LBTEN);
124}
125
126static inline void lose_lbt(int save)
127{
128	preempt_disable();
129	lose_lbt_inatomic(save, current);
130	preempt_enable();
131}
132
133#else
134static inline void own_lbt_inatomic(int restore) {}
135static inline void lose_lbt_inatomic(int save, struct task_struct *tsk) {}
136static inline void init_lbt(void) {}
137static inline void lose_lbt(int save) {}
138#endif
139
140static inline int thread_lbt_context_live(void)
141{
142	if (__builtin_constant_p(cpu_has_lbt) && !cpu_has_lbt)
143		return 0;
144
145	return test_thread_flag(TIF_LBT_CTX_LIVE);
146}
147
148#endif
149