1/*
2 * Copyright (c) 2006-2022, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date         Author    Notes
8 * 2010-01-25   Bernard      first version
9 * 2012-06-01   aozima       set pendsv priority to 0xFF.
10 * 2012-08-17   aozima       fixed bug: store r8 - r11.
11 * 2013-02-20   aozima       port to gcc.
12 * 2013-06-18   aozima       add restore MSP feature.
13 * 2013-11-04   bright       fixed hardfault bug for gcc.
14 */
15
16    .cpu    cortex-m0
17    .fpu    softvfp
18    .syntax unified
19    .thumb
20    .text
21
22    .equ    SCB_VTOR, 0xE000ED08            /* Vector Table Offset Register */
23    .equ    NVIC_INT_CTRL, 0xE000ED04       /* interrupt control state register */
24    .equ    NVIC_SHPR3, 0xE000ED20          /* system priority register (3) */
25    .equ    NVIC_PENDSV_PRI, 0xFFFF0000     /* PendSV and SysTick priority value (lowest) */
26    .equ    NVIC_PENDSVSET, 0x10000000      /* value to trigger PendSV exception */
27
28#include "../rtconfig.h"
29
30#ifdef RT_USING_SMP
31    .equ    SIO_CPUID, 0xd0000000 /* CPUID */
32    #define rt_hw_interrupt_disable rt_hw_local_irq_disable
33    #define rt_hw_interrupt_enable  rt_hw_local_irq_enable
34#endif
35
36/*
37 * rt_base_t rt_hw_interrupt_disable();
38 */
39    .global rt_hw_interrupt_disable
40    .type rt_hw_interrupt_disable, %function
41rt_hw_interrupt_disable:
42    MRS     R0, PRIMASK
43    CPSID   I
44    BX      LR
45
46/*
47 * void rt_hw_interrupt_enable(rt_base_t level);
48 */
49    .global rt_hw_interrupt_enable
50    .type rt_hw_interrupt_enable, %function
51rt_hw_interrupt_enable:
52    MSR     PRIMASK, R0
53    BX      LR
54
55/*
56 * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
57 * R0 --> from
58 * R1 --> to
59 */
60    .global rt_hw_context_switch_interrupt
61    .type rt_hw_context_switch_interrupt, %function
62    .global rt_hw_context_switch
63    .type rt_hw_context_switch, %function
64rt_hw_context_switch_interrupt:
65rt_hw_context_switch:
66#ifndef RT_USING_SMP
67    /* set rt_thread_switch_interrupt_flag to 1 */
68    LDR     R2, =rt_thread_switch_interrupt_flag
69    LDR     R3, [R2]
70    CMP     R3, #1
71    BEQ     _reswitch
72    MOVS    R3, #1
73    STR     R3, [R2]
74
75    LDR     R2, =rt_interrupt_from_thread   /* set rt_interrupt_from_thread */
76    STR     R0, [R2]
77
78_reswitch:
79    LDR     R2, =rt_interrupt_to_thread     /* set rt_interrupt_to_thread */
80    STR     R1, [R2]
81#else
82    /* context_switch_to smp */
83    PUSH    {LR}
84    BL      __rt_cpu_switch
85    POP     {R2}
86#endif
87
88    LDR     R0, =NVIC_INT_CTRL           /* trigger the PendSV exception (causes context switch) */
89    LDR     R1, =NVIC_PENDSVSET
90    STR     R1, [R0]
91#ifndef RT_USING_SMP
92    BX      LR
93#else
94    BX      R2
95#endif
96
97/* R0 --> switch from thread stack
98 * R1 --> switch to thread stack
99 * psr, pc, LR, R12, R3, R2, R1, R0 are pushed into [from] stack
100 */
101    .global PendSV_Handler
102    .type PendSV_Handler, %function
103PendSV_Handler:
104    /* disable interrupt to protect context switch */
105    MRS     R2, PRIMASK
106    CPSID   I
107
108#ifndef RT_USING_SMP
109    /* get rt_thread_switch_interrupt_flag */
110    LDR     R0, =rt_thread_switch_interrupt_flag
111#else
112    LDR     R0, =SIO_CPUID
113    LDR     R1, [R0]
114    LDR     R3, =rt_thread_switch_array
115    CMP     R1, #0
116    BEQ     cpu0_info
117    ADDS    R3, #12
118cpu0_info:
119    MOV     R0, R3
120#endif
121    LDR     R1, [R0]
122
123    CMP     R1, #0x00
124    BEQ     pendsv_exit     /* pendsv already handled */
125
126    /* clear rt_thread_switch_interrupt_flag to 0 */
127    MOVS    R1, #0
128    STR     R1, [R0]
129
130#ifndef RT_USING_SMP
131    LDR     R0, =rt_interrupt_from_thread
132#else
133    ADDS    R0, #4
134#endif
135    LDR     R1, [R0]
136
137    CMP     R1, #0x00
138    BEQ     switch_to_thread    /* skip register save at the first time */
139
140    MRS     R1, PSP                 /* get from thread stack pointer */
141
142    SUBS    R1, R1, #0x20           /* space for {R4 - R7} and {R8 - R11} */
143    LDR     R0, [R0]
144    STR     R1, [R0]                /* update from thread stack pointer */
145
146    STMIA   R1!, {R4 - R7}          /* push thread {R4 - R7} register to thread stack */
147
148    MOV     R4, R8                  /* mov thread {R8 - R11} to {R4 - R7} */
149    MOV     R5, R9
150    MOV     R6, R10
151    MOV     R7, R11
152    STMIA   R1!, {R4 - R7}          /* push thread {R8 - R11} high register to thread stack */
153switch_to_thread:
154
155#ifndef RT_USING_SMP
156    LDR     R1, =rt_interrupt_to_thread
157#else
158    MOV     R1, R3
159    ADDS    R1, #8
160#endif
161    LDR     R1, [R1]
162
163    LDR     R1, [R1]                /* load thread stack pointer */
164
165    LDMIA   R1!, {R4 - R7}          /* pop thread {R4 - R7} register from thread stack */
166    PUSH    {R4 - R7}               /* push {R4 - R7} to MSP for copy {R8 - R11} */
167
168    LDMIA   R1!, {R4 - R7}          /* pop thread {R8 - R11} high register from thread stack to {R4 - R7} */
169    MOV     R8,  R4                 /* mov {R4 - R7} to {R8 - R11} */
170    MOV     R9,  R5
171    MOV     R10, R6
172    MOV     R11, R7
173
174    POP     {R4 - R7}               /* pop {R4 - R7} from MSP */
175
176    MSR     PSP, R1                 /* update stack pointer */
177
178pendsv_exit:
179    /* restore interrupt */
180    MSR     PRIMASK, R2
181
182    MOVS    R0, #0x03
183    RSBS    R0, R0, #0x00
184    BX      R0
185/*
186 * void rt_hw_context_switch_to(rt_uint32 to);
187 * R0 --> to
188 */
189    .global rt_hw_context_switch_to
190    .type rt_hw_context_switch_to, %function
191rt_hw_context_switch_to:
192#ifndef RT_USING_SMP
193    LDR     R1, =rt_interrupt_to_thread
194    STR     R0, [R1]
195
196    /* set from thread to 0 */
197    LDR     R1, =rt_interrupt_from_thread
198    MOVS    R0, #0
199    STR     R0, [R1]
200
201    /* set interrupt flag to 1 */
202    LDR     R1, =rt_thread_switch_interrupt_flag
203    MOVS    R0, #1
204    STR     R0, [R1]
205#else
206    /* context_switch_to smp */
207    MOV     R2,R1
208    MOV     R1,R0
209    MOVS    R0,#0
210    BL      __rt_cpu_switch
211#endif
212
213    /* set the PendSV and SysTick exception priority */
214    LDR     R0, =NVIC_SHPR3
215    LDR     R1, =NVIC_PENDSV_PRI
216    LDR     R2, [R0,#0x00]       /* read */
217    ORRS    R1, R1, R2             /* modify */
218    STR     R1, [R0]             /* write-back */
219
220    LDR     R0, =NVIC_INT_CTRL               /* trigger the PendSV exception (causes context switch) */
221    LDR     R1, =NVIC_PENDSVSET
222    STR     R1, [R0]
223    NOP
224    /* restore MSP */
225    LDR     R0, =SCB_VTOR
226    LDR     R0, [R0]
227    LDR     R0, [R0]
228    NOP
229    MSR     MSP, R0
230
231    /* enable interrupts at processor level */
232    CPSIE   I
233
234    /* ensure PendSV exception taken place before subsequent operation */
235    DSB
236    ISB
237
238    /* never reach here! */
239
240/* compatible with old version */
241    .global rt_hw_interrupt_thread_switch
242    .type rt_hw_interrupt_thread_switch, %function
243rt_hw_interrupt_thread_switch:
244    BX      LR
245    NOP
246
247    .global HardFault_Handler
248    .type HardFault_Handler, %function
249HardFault_Handler:
250    /* get current context */
251    MRS     R0, PSP                 /* get fault thread stack pointer */
252    PUSH    {LR}
253    BL      rt_hw_hard_fault_exception
254    POP     {PC}
255
256
257/*
258 * rt_uint32_t rt_hw_interrupt_check(void);
259 * R0 --> state
260 */
261    .global rt_hw_interrupt_check
262    .type rt_hw_interrupt_check, %function
263rt_hw_interrupt_check:
264    MRS     R0, IPSR
265    BX      LR
266