/* SPDX-License-Identifier: GPL-2.0 */
// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.

#include <linux/linkage.h>
#include <asm/ftrace.h>
#include <abi/entry.h>
#include <asm/asm-offsets.h>

/*
 * csky-gcc with -pg will put the following asm after prologue:
 *      push	r15
 *      jsri	_mcount
 *
 * stack layout after mcount_enter in _mcount():
 *
 * current sp => 0:+-------+
 *                 | a0-a3 | -> must save all argument regs
 *             +16:+-------+
 *                 | lr    | -> _mcount lr (instrumente function's pc)
 *             +20:+-------+
 *                 | fp=r8 | -> instrumented function fp
 *             +24:+-------+
 *                 | plr   | -> instrumented function lr (parent's pc)
 *                 +-------+
 */

.macro mcount_enter
	subi	sp, 24
	stw	a0, (sp, 0)
	stw	a1, (sp, 4)
	stw	a2, (sp, 8)
	stw	a3, (sp, 12)
	stw	lr, (sp, 16)
	stw	r8, (sp, 20)
.endm

.macro mcount_exit
	ldw	a0, (sp, 0)
	ldw	a1, (sp, 4)
	ldw	a2, (sp, 8)
	ldw	a3, (sp, 12)
	ldw	t1, (sp, 16)
	ldw	r8, (sp, 20)
	ldw	lr, (sp, 24)
	addi	sp, 28
	jmp	t1
.endm

.macro mcount_enter_regs
	subi	sp, 8
	stw	lr, (sp, 0)
	stw	r8, (sp, 4)
	SAVE_REGS_FTRACE
.endm

.macro mcount_exit_regs
	RESTORE_REGS_FTRACE
	subi	sp, 152
	ldw	t1, (sp, 4)
	addi	sp, 152
	ldw	r8, (sp, 4)
	ldw	lr, (sp, 8)
	addi	sp, 12
	jmp	t1
.endm

.macro save_return_regs
	subi	sp, 16
	stw	a0, (sp, 0)
	stw	a1, (sp, 4)
	stw	a2, (sp, 8)
	stw	a3, (sp, 12)
.endm

.macro restore_return_regs
	mov	lr, a0
	ldw	a0, (sp, 0)
	ldw	a1, (sp, 4)
	ldw	a2, (sp, 8)
	ldw	a3, (sp, 12)
	addi	sp, 16
.endm

.macro nop32_stub
	nop32
	nop32
	nop32
.endm

ENTRY(ftrace_stub)
	jmp	lr
END(ftrace_stub)

#ifndef CONFIG_DYNAMIC_FTRACE
ENTRY(_mcount)
	mcount_enter

	/* r26 is link register, only used with jsri translation */
	lrw	r26, ftrace_trace_function
	ldw	r26, (r26, 0)
	lrw	a1, ftrace_stub
	cmpne	r26, a1
	bf	skip_ftrace

	mov	a0, lr
	subi	a0, 4
	ldw	a1, (sp, 24)
	lrw	a2, function_trace_op
	ldw	a2, (a2, 0)

	jsr	r26

#ifndef CONFIG_FUNCTION_GRAPH_TRACER
skip_ftrace:
	mcount_exit
#else
skip_ftrace:
	lrw	a0, ftrace_graph_return
	ldw	a0, (a0, 0)
	lrw	a1, ftrace_stub
	cmpne	a0, a1
	bt	ftrace_graph_caller

	lrw	a0, ftrace_graph_entry
	ldw	a0, (a0, 0)
	lrw	a1, ftrace_graph_entry_stub
	cmpne	a0, a1
	bt	ftrace_graph_caller

	mcount_exit
#endif
END(_mcount)
#else /* CONFIG_DYNAMIC_FTRACE */
ENTRY(_mcount)
	mov	t1, lr
	ldw	lr, (sp, 0)
	addi	sp, 4
	jmp	t1
ENDPROC(_mcount)

ENTRY(ftrace_caller)
	mcount_enter

	ldw	a0, (sp, 16)
	subi	a0, 4
	ldw	a1, (sp, 24)
	lrw	a2, function_trace_op
	ldw	a2, (a2, 0)

	nop
GLOBAL(ftrace_call)
	nop32_stub

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
	nop
GLOBAL(ftrace_graph_call)
	nop32_stub
#endif

	mcount_exit
ENDPROC(ftrace_caller)
#endif /* CONFIG_DYNAMIC_FTRACE */

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
ENTRY(ftrace_graph_caller)
	mov	a0, sp
	addi	a0, 24
	ldw	a1, (sp, 16)
	subi	a1, 4
	mov	a2, r8
	lrw	r26, prepare_ftrace_return
	jsr	r26
	mcount_exit
END(ftrace_graph_caller)

ENTRY(return_to_handler)
	save_return_regs
	mov	a0, r8
	jsri	ftrace_return_to_handler
	restore_return_regs
	jmp	lr
END(return_to_handler)
#endif

#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
ENTRY(ftrace_regs_caller)
	mcount_enter_regs

	lrw	t1, PT_FRAME_SIZE
	add	t1, sp

	ldw	a0, (t1, 0)
	subi	a0, 4
	ldw	a1, (t1, 8)
	lrw	a2, function_trace_op
	ldw	a2, (a2, 0)
	mov	a3, sp

	nop
GLOBAL(ftrace_regs_call)
	nop32_stub

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
	nop
GLOBAL(ftrace_graph_regs_call)
	nop32_stub
#endif

	mcount_exit_regs
ENDPROC(ftrace_regs_caller)
#endif /* CONFIG_DYNAMIC_FTRACE */