xref: /openbmc/linux/arch/riscv/include/asm/vector.h (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1dc6667a4SGuo Ren /* SPDX-License-Identifier: GPL-2.0-or-later */
2dc6667a4SGuo Ren /*
3dc6667a4SGuo Ren  * Copyright (C) 2020 SiFive
4dc6667a4SGuo Ren  */
5dc6667a4SGuo Ren 
6dc6667a4SGuo Ren #ifndef __ASM_RISCV_VECTOR_H
7dc6667a4SGuo Ren #define __ASM_RISCV_VECTOR_H
8dc6667a4SGuo Ren 
9dc6667a4SGuo Ren #include <linux/types.h>
107017858eSGreentime Hu #include <uapi/asm-generic/errno.h>
11dc6667a4SGuo Ren 
12dc6667a4SGuo Ren #ifdef CONFIG_RISCV_ISA_V
13dc6667a4SGuo Ren 
1403c3fcd9SGreentime Hu #include <linux/stringify.h>
153a2df632SGreentime Hu #include <linux/sched.h>
163a2df632SGreentime Hu #include <linux/sched/task_stack.h>
173a2df632SGreentime Hu #include <asm/ptrace.h>
18dc6667a4SGuo Ren #include <asm/hwcap.h>
190a3381a0SGreentime Hu #include <asm/csr.h>
2003c3fcd9SGreentime Hu #include <asm/asm.h>
21dc6667a4SGuo Ren 
227017858eSGreentime Hu extern unsigned long riscv_v_vsize;
237017858eSGreentime Hu int riscv_v_setup_vsize(void);
24cd054837SAndy Chiu bool riscv_v_first_use_handler(struct pt_regs *regs);
257017858eSGreentime Hu 
has_vector(void)26dc6667a4SGuo Ren static __always_inline bool has_vector(void)
27dc6667a4SGuo Ren {
28dc6667a4SGuo Ren 	return riscv_has_extension_unlikely(RISCV_ISA_EXT_v);
29dc6667a4SGuo Ren }
30dc6667a4SGuo Ren 
__riscv_v_vstate_clean(struct pt_regs * regs)3103c3fcd9SGreentime Hu static inline void __riscv_v_vstate_clean(struct pt_regs *regs)
3203c3fcd9SGreentime Hu {
3303c3fcd9SGreentime Hu 	regs->status = (regs->status & ~SR_VS) | SR_VS_CLEAN;
3403c3fcd9SGreentime Hu }
3503c3fcd9SGreentime Hu 
__riscv_v_vstate_dirty(struct pt_regs * regs)369657e9b7SBjörn Töpel static inline void __riscv_v_vstate_dirty(struct pt_regs *regs)
379657e9b7SBjörn Töpel {
389657e9b7SBjörn Töpel 	regs->status = (regs->status & ~SR_VS) | SR_VS_DIRTY;
399657e9b7SBjörn Töpel }
409657e9b7SBjörn Töpel 
riscv_v_vstate_off(struct pt_regs * regs)4103c3fcd9SGreentime Hu static inline void riscv_v_vstate_off(struct pt_regs *regs)
4203c3fcd9SGreentime Hu {
4303c3fcd9SGreentime Hu 	regs->status = (regs->status & ~SR_VS) | SR_VS_OFF;
4403c3fcd9SGreentime Hu }
4503c3fcd9SGreentime Hu 
riscv_v_vstate_on(struct pt_regs * regs)4603c3fcd9SGreentime Hu static inline void riscv_v_vstate_on(struct pt_regs *regs)
4703c3fcd9SGreentime Hu {
4803c3fcd9SGreentime Hu 	regs->status = (regs->status & ~SR_VS) | SR_VS_INITIAL;
4903c3fcd9SGreentime Hu }
5003c3fcd9SGreentime Hu 
riscv_v_vstate_query(struct pt_regs * regs)5103c3fcd9SGreentime Hu static inline bool riscv_v_vstate_query(struct pt_regs *regs)
5203c3fcd9SGreentime Hu {
5303c3fcd9SGreentime Hu 	return (regs->status & SR_VS) != 0;
5403c3fcd9SGreentime Hu }
5503c3fcd9SGreentime Hu 
riscv_v_enable(void)560a3381a0SGreentime Hu static __always_inline void riscv_v_enable(void)
570a3381a0SGreentime Hu {
580a3381a0SGreentime Hu 	csr_set(CSR_SSTATUS, SR_VS);
590a3381a0SGreentime Hu }
600a3381a0SGreentime Hu 
riscv_v_disable(void)610a3381a0SGreentime Hu static __always_inline void riscv_v_disable(void)
620a3381a0SGreentime Hu {
630a3381a0SGreentime Hu 	csr_clear(CSR_SSTATUS, SR_VS);
640a3381a0SGreentime Hu }
650a3381a0SGreentime Hu 
__vstate_csr_save(struct __riscv_v_ext_state * dest)6603c3fcd9SGreentime Hu static __always_inline void __vstate_csr_save(struct __riscv_v_ext_state *dest)
6703c3fcd9SGreentime Hu {
6803c3fcd9SGreentime Hu 	asm volatile (
6903c3fcd9SGreentime Hu 		"csrr	%0, " __stringify(CSR_VSTART) "\n\t"
7003c3fcd9SGreentime Hu 		"csrr	%1, " __stringify(CSR_VTYPE) "\n\t"
7103c3fcd9SGreentime Hu 		"csrr	%2, " __stringify(CSR_VL) "\n\t"
7203c3fcd9SGreentime Hu 		"csrr	%3, " __stringify(CSR_VCSR) "\n\t"
73*c35f3aa3SAndy Chiu 		"csrr	%4, " __stringify(CSR_VLENB) "\n\t"
7403c3fcd9SGreentime Hu 		: "=r" (dest->vstart), "=r" (dest->vtype), "=r" (dest->vl),
75*c35f3aa3SAndy Chiu 		  "=r" (dest->vcsr), "=r" (dest->vlenb) : :);
7603c3fcd9SGreentime Hu }
7703c3fcd9SGreentime Hu 
__vstate_csr_restore(struct __riscv_v_ext_state * src)7803c3fcd9SGreentime Hu static __always_inline void __vstate_csr_restore(struct __riscv_v_ext_state *src)
7903c3fcd9SGreentime Hu {
8003c3fcd9SGreentime Hu 	asm volatile (
8103c3fcd9SGreentime Hu 		".option push\n\t"
8203c3fcd9SGreentime Hu 		".option arch, +v\n\t"
8303c3fcd9SGreentime Hu 		"vsetvl	 x0, %2, %1\n\t"
8403c3fcd9SGreentime Hu 		".option pop\n\t"
8503c3fcd9SGreentime Hu 		"csrw	" __stringify(CSR_VSTART) ", %0\n\t"
8603c3fcd9SGreentime Hu 		"csrw	" __stringify(CSR_VCSR) ", %3\n\t"
8703c3fcd9SGreentime Hu 		: : "r" (src->vstart), "r" (src->vtype), "r" (src->vl),
8803c3fcd9SGreentime Hu 		    "r" (src->vcsr) :);
8903c3fcd9SGreentime Hu }
9003c3fcd9SGreentime Hu 
__riscv_v_vstate_save(struct __riscv_v_ext_state * save_to,void * datap)9103c3fcd9SGreentime Hu static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to,
9203c3fcd9SGreentime Hu 					 void *datap)
9303c3fcd9SGreentime Hu {
9403c3fcd9SGreentime Hu 	unsigned long vl;
9503c3fcd9SGreentime Hu 
9603c3fcd9SGreentime Hu 	riscv_v_enable();
9703c3fcd9SGreentime Hu 	__vstate_csr_save(save_to);
9803c3fcd9SGreentime Hu 	asm volatile (
9903c3fcd9SGreentime Hu 		".option push\n\t"
10003c3fcd9SGreentime Hu 		".option arch, +v\n\t"
10103c3fcd9SGreentime Hu 		"vsetvli	%0, x0, e8, m8, ta, ma\n\t"
10203c3fcd9SGreentime Hu 		"vse8.v		v0, (%1)\n\t"
10303c3fcd9SGreentime Hu 		"add		%1, %1, %0\n\t"
10403c3fcd9SGreentime Hu 		"vse8.v		v8, (%1)\n\t"
10503c3fcd9SGreentime Hu 		"add		%1, %1, %0\n\t"
10603c3fcd9SGreentime Hu 		"vse8.v		v16, (%1)\n\t"
10703c3fcd9SGreentime Hu 		"add		%1, %1, %0\n\t"
10803c3fcd9SGreentime Hu 		"vse8.v		v24, (%1)\n\t"
10903c3fcd9SGreentime Hu 		".option pop\n\t"
11003c3fcd9SGreentime Hu 		: "=&r" (vl) : "r" (datap) : "memory");
11103c3fcd9SGreentime Hu 	riscv_v_disable();
11203c3fcd9SGreentime Hu }
11303c3fcd9SGreentime Hu 
__riscv_v_vstate_restore(struct __riscv_v_ext_state * restore_from,void * datap)11403c3fcd9SGreentime Hu static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_from,
11503c3fcd9SGreentime Hu 					    void *datap)
11603c3fcd9SGreentime Hu {
11703c3fcd9SGreentime Hu 	unsigned long vl;
11803c3fcd9SGreentime Hu 
11903c3fcd9SGreentime Hu 	riscv_v_enable();
12003c3fcd9SGreentime Hu 	asm volatile (
12103c3fcd9SGreentime Hu 		".option push\n\t"
12203c3fcd9SGreentime Hu 		".option arch, +v\n\t"
12303c3fcd9SGreentime Hu 		"vsetvli	%0, x0, e8, m8, ta, ma\n\t"
12403c3fcd9SGreentime Hu 		"vle8.v		v0, (%1)\n\t"
12503c3fcd9SGreentime Hu 		"add		%1, %1, %0\n\t"
12603c3fcd9SGreentime Hu 		"vle8.v		v8, (%1)\n\t"
12703c3fcd9SGreentime Hu 		"add		%1, %1, %0\n\t"
12803c3fcd9SGreentime Hu 		"vle8.v		v16, (%1)\n\t"
12903c3fcd9SGreentime Hu 		"add		%1, %1, %0\n\t"
13003c3fcd9SGreentime Hu 		"vle8.v		v24, (%1)\n\t"
13103c3fcd9SGreentime Hu 		".option pop\n\t"
13203c3fcd9SGreentime Hu 		: "=&r" (vl) : "r" (datap) : "memory");
13303c3fcd9SGreentime Hu 	__vstate_csr_restore(restore_from);
13403c3fcd9SGreentime Hu 	riscv_v_disable();
13503c3fcd9SGreentime Hu }
13603c3fcd9SGreentime Hu 
__riscv_v_vstate_discard(void)1379657e9b7SBjörn Töpel static inline void __riscv_v_vstate_discard(void)
1389657e9b7SBjörn Töpel {
1399657e9b7SBjörn Töpel 	unsigned long vl, vtype_inval = 1UL << (BITS_PER_LONG - 1);
1409657e9b7SBjörn Töpel 
1419657e9b7SBjörn Töpel 	riscv_v_enable();
1429657e9b7SBjörn Töpel 	asm volatile (
1439657e9b7SBjörn Töpel 		".option push\n\t"
1449657e9b7SBjörn Töpel 		".option arch, +v\n\t"
1459657e9b7SBjörn Töpel 		"vsetvli	%0, x0, e8, m8, ta, ma\n\t"
1469657e9b7SBjörn Töpel 		"vmv.v.i	v0, -1\n\t"
1479657e9b7SBjörn Töpel 		"vmv.v.i	v8, -1\n\t"
1489657e9b7SBjörn Töpel 		"vmv.v.i	v16, -1\n\t"
1499657e9b7SBjörn Töpel 		"vmv.v.i	v24, -1\n\t"
1509657e9b7SBjörn Töpel 		"vsetvl		%0, x0, %1\n\t"
1519657e9b7SBjörn Töpel 		".option pop\n\t"
1529657e9b7SBjörn Töpel 		: "=&r" (vl) : "r" (vtype_inval) : "memory");
1539657e9b7SBjörn Töpel 	riscv_v_disable();
1549657e9b7SBjörn Töpel }
1559657e9b7SBjörn Töpel 
riscv_v_vstate_discard(struct pt_regs * regs)1569657e9b7SBjörn Töpel static inline void riscv_v_vstate_discard(struct pt_regs *regs)
1579657e9b7SBjörn Töpel {
1589657e9b7SBjörn Töpel 	if ((regs->status & SR_VS) == SR_VS_OFF)
1599657e9b7SBjörn Töpel 		return;
1609657e9b7SBjörn Töpel 
1619657e9b7SBjörn Töpel 	__riscv_v_vstate_discard();
1629657e9b7SBjörn Töpel 	__riscv_v_vstate_dirty(regs);
1639657e9b7SBjörn Töpel }
1649657e9b7SBjörn Töpel 
riscv_v_vstate_save(struct task_struct * task,struct pt_regs * regs)1653a2df632SGreentime Hu static inline void riscv_v_vstate_save(struct task_struct *task,
1663a2df632SGreentime Hu 				       struct pt_regs *regs)
1673a2df632SGreentime Hu {
1683a2df632SGreentime Hu 	if ((regs->status & SR_VS) == SR_VS_DIRTY) {
1693a2df632SGreentime Hu 		struct __riscv_v_ext_state *vstate = &task->thread.vstate;
1703a2df632SGreentime Hu 
1713a2df632SGreentime Hu 		__riscv_v_vstate_save(vstate, vstate->datap);
1723a2df632SGreentime Hu 		__riscv_v_vstate_clean(regs);
1733a2df632SGreentime Hu 	}
1743a2df632SGreentime Hu }
1753a2df632SGreentime Hu 
riscv_v_vstate_restore(struct task_struct * task,struct pt_regs * regs)1763a2df632SGreentime Hu static inline void riscv_v_vstate_restore(struct task_struct *task,
1773a2df632SGreentime Hu 					  struct pt_regs *regs)
1783a2df632SGreentime Hu {
1793a2df632SGreentime Hu 	if ((regs->status & SR_VS) != SR_VS_OFF) {
1803a2df632SGreentime Hu 		struct __riscv_v_ext_state *vstate = &task->thread.vstate;
1813a2df632SGreentime Hu 
1823a2df632SGreentime Hu 		__riscv_v_vstate_restore(vstate, vstate->datap);
1833a2df632SGreentime Hu 		__riscv_v_vstate_clean(regs);
1843a2df632SGreentime Hu 	}
1853a2df632SGreentime Hu }
1863a2df632SGreentime Hu 
__switch_to_vector(struct task_struct * prev,struct task_struct * next)1873a2df632SGreentime Hu static inline void __switch_to_vector(struct task_struct *prev,
1883a2df632SGreentime Hu 				      struct task_struct *next)
1893a2df632SGreentime Hu {
1903a2df632SGreentime Hu 	struct pt_regs *regs;
1913a2df632SGreentime Hu 
1923a2df632SGreentime Hu 	regs = task_pt_regs(prev);
1933a2df632SGreentime Hu 	riscv_v_vstate_save(prev, regs);
1943a2df632SGreentime Hu 	riscv_v_vstate_restore(next, task_pt_regs(next));
1953a2df632SGreentime Hu }
1963a2df632SGreentime Hu 
1971fd96a3eSAndy Chiu void riscv_v_vstate_ctrl_init(struct task_struct *tsk);
1981fd96a3eSAndy Chiu bool riscv_v_vstate_ctrl_user_allowed(void);
1991fd96a3eSAndy Chiu 
200dc6667a4SGuo Ren #else /* ! CONFIG_RISCV_ISA_V  */
201dc6667a4SGuo Ren 
2027017858eSGreentime Hu struct pt_regs;
2037017858eSGreentime Hu 
riscv_v_setup_vsize(void)2047017858eSGreentime Hu static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; }
has_vector(void)205dc6667a4SGuo Ren static __always_inline bool has_vector(void) { return false; }
riscv_v_first_use_handler(struct pt_regs * regs)206cd054837SAndy Chiu static inline bool riscv_v_first_use_handler(struct pt_regs *regs) { return false; }
riscv_v_vstate_query(struct pt_regs * regs)20703c3fcd9SGreentime Hu static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; }
riscv_v_vstate_ctrl_user_allowed(void)2081fd96a3eSAndy Chiu static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; }
2097017858eSGreentime Hu #define riscv_v_vsize (0)
2109657e9b7SBjörn Töpel #define riscv_v_vstate_discard(regs)		do {} while (0)
2113a2df632SGreentime Hu #define riscv_v_vstate_save(task, regs)		do {} while (0)
2123a2df632SGreentime Hu #define riscv_v_vstate_restore(task, regs)	do {} while (0)
2133a2df632SGreentime Hu #define __switch_to_vector(__prev, __next)	do {} while (0)
21403c3fcd9SGreentime Hu #define riscv_v_vstate_off(regs)		do {} while (0)
21503c3fcd9SGreentime Hu #define riscv_v_vstate_on(regs)			do {} while (0)
216dc6667a4SGuo Ren 
217dc6667a4SGuo Ren #endif /* CONFIG_RISCV_ISA_V */
218dc6667a4SGuo Ren 
219dc6667a4SGuo Ren #endif /* ! __ASM_RISCV_VECTOR_H */
220