;/*
; * File      : context_iar.S
; * This file is part of RT-Thread RTOS
; * COPYRIGHT (C) 2009, RT-Thread Development Team
; *
; * The license and distribution terms for this file may be
; * found in the file LICENSE in this distribution or at
; * http://www.rt-thread.org/license/LICENSE
; *
; * Change Logs:
; * Date           Author       Notes
; * 2009-01-17     Bernard      first version
; * 2009-09-27     Bernard      add protect when contex switch occurs
; */

;/**
; * @addtogroup STM32
; */
;/*@{*/

NVIC_INT_CTRL   EQU     0xE000ED04               ; interrupt control state register
NVIC_SYSPRI2    EQU     0xE000ED20               ; system priority register (2)
NVIC_PENDSV_PRI EQU     0x00FF0000               ; PendSV priority value (lowest)
NVIC_PENDSVSET  EQU     0x10000000               ; value to trigger PendSV exception

    SECTION    .text:CODE(2)
    THUMB
    REQUIRE8
    PRESERVE8

	IMPORT rt_thread_switch_interrput_flag
	IMPORT rt_interrupt_from_thread
	IMPORT rt_interrupt_to_thread

;/*
; * rt_base_t rt_hw_interrupt_disable();
; */
	EXPORT rt_hw_interrupt_disable
rt_hw_interrupt_disable:
	MRS		r0, PRIMASK
	CPSID   I
	BX		LR

;/*
; * void rt_hw_interrupt_enable(rt_base_t level);
; */
	EXPORT  rt_hw_interrupt_enable
rt_hw_interrupt_enable:
	MSR		PRIMASK, r0
	BX      LR

;/*
; * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
; * r0 --> from
; * r1 --> to
; */
    EXPORT rt_hw_context_switch_interrupt
	EXPORT rt_hw_context_switch
rt_hw_context_switch_interrupt:
rt_hw_context_switch:
	; set rt_thread_switch_interrput_flag to 1
	LDR 	r2, =rt_thread_switch_interrput_flag
	LDR 	r3, [r2]
	CMP 	r3, #1
	BEQ 	_reswitch
	MOV 	r3, #1
	STR 	r3, [r2]

	LDR 	r2, =rt_interrupt_from_thread	; set rt_interrupt_from_thread
	STR 	r0, [r2]

_reswitch
	LDR 	r2, =rt_interrupt_to_thread		; set rt_interrupt_to_thread
	STR 	r1, [r2]

    LDR     r0, =NVIC_INT_CTRL      		; trigger the PendSV exception (causes context switch)
    LDR     r1, =NVIC_PENDSVSET
	STR     r1, [r0]
	BX      LR

; r0 --> swith from thread stack
; r1 --> swith to thread stack
; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
	EXPORT rt_hw_pend_sv
rt_hw_pend_sv:

	; disable interrupt to protect context switch
	MRS		r2, PRIMASK
	CPSID   I

	; get rt_thread_switch_interrupt_flag
	LDR		r0, =rt_thread_switch_interrput_flag
	LDR		r1, [r0]
	CBZ		r1, pendsv_exit			; pendsv already handled

	; clear rt_thread_switch_interrput_flag to 0
	MOV		r1, #0x00
	STR		r1, [r0]

	LDR		r0, =rt_interrupt_from_thread
	LDR		r1, [r0]
	CBZ		r1, swtich_to_thread    ; skip register save at the first time

	MRS     r1, psp                 ; get from thread stack pointer
	STMFD	r1!, {r4 - r11}			; push r4 - r11 register
	LDR		r0, [r0]
	STR		r1, [r0]				; update from thread stack pointer

swtich_to_thread
	LDR		r1, =rt_interrupt_to_thread
	LDR		r1, [r1]
	LDR		r1, [r1]				; load thread stack pointer

	LDMFD	r1!, {r4 - r11}			; pop r4 - r11 register
	MSR		psp, r1					; update stack pointer

pendsv_exit
	; restore interrupt
	MSR		PRIMASK, r2

	ORR     lr, lr, #0x04
	BX		lr

;/*
; * void rt_hw_context_switch_to(rt_uint32 to);
; * r0 --> to
; */
	EXPORT rt_hw_context_switch_to
rt_hw_context_switch_to:
	LDR		r1, =rt_interrupt_to_thread
	STR		r0, [r1]

	; set from thread to 0
	LDR		r1, =rt_interrupt_from_thread
	MOV		r0, #0x0
	STR		r0, [r1]

	; set interrupt flag to 1
	LDR 	r1, =rt_thread_switch_interrput_flag
	MOV 	r0, #1
	STR 	r0, [r1]

	; set the PendSV exception priority
    LDR     r0, =NVIC_SYSPRI2
    LDR     r1, =NVIC_PENDSV_PRI
    LDR.W   r2, [r0,#0x00]       ; read
    ORR     r1,r1,r2             ; modify
    STR     r1, [r0]             ; write-back

    LDR     r0, =NVIC_INT_CTRL      ; trigger the PendSV exception (causes context switch)
    LDR     r1, =NVIC_PENDSVSET
    STR     r1, [r0]

    CPSIE   I                       ; enable interrupts at processor level

	; never reach here!

; compatible with old version
	EXPORT rt_hw_interrupt_thread_switch
rt_hw_interrupt_thread_switch:
	BX		lr

    END