1;/***************************************************************************
2; * Copyright (c) 2024 Microsoft Corporation
3; *
4; * This program and the accompanying materials are made available under the
5; * terms of the MIT License which is available at
6; * https://opensource.org/licenses/MIT.
7; *
8; * SPDX-License-Identifier: MIT
9; **************************************************************************/
10;
11;
12;/**************************************************************************/
13;/**************************************************************************/
14;/**                                                                       */
15;/** ThreadX Component                                                     */
16;/**                                                                       */
17;/**   Thread                                                              */
18;/**                                                                       */
19;/**************************************************************************/
20;/**************************************************************************/
21;
22;
23;#define TX_SOURCE_CODE
24;
25;
26;/* Include necessary system files.  */
27;
28;#include "tx_api.h"
29;#include "tx_thread.h"
30;#include "tx_timer.h"
31;
32;
33SVC_MODE        EQU     0xD3                    ; SVC mode
34FIQ_MODE        EQU     0xD1                    ; FIQ mode
35    IF  :DEF:TX_ENABLE_FIQ_SUPPORT
36DISABLE_INTS    EQU     0xC0                    ; Disable IRQ & FIQ interrupts
37    ELSE
38DISABLE_INTS    EQU     0x80                    ; Disable IRQ interrupts
39    ENDIF
40MODE_MASK       EQU     0x1F                    ; Mode mask
41THUMB_MASK      EQU     0x20                    ; Thumb bit mask
42IRQ_MODE_BITS   EQU     0x12                    ; IRQ mode bits
43SVC_MODE_BITS   EQU     0x13                    ; SVC mode value
44;
45;
46    IMPORT      _tx_thread_system_state
47    IMPORT      _tx_thread_current_ptr
48    IMPORT      _tx_thread_system_stack_ptr
49    IMPORT      _tx_thread_execute_ptr
50    IMPORT      _tx_timer_time_slice
51    IMPORT      _tx_thread_schedule
52    IMPORT      _tx_thread_preempt_disable
53    IF :DEF:TX_ENABLE_EXECUTION_CHANGE_NOTIFY
54    IMPORT      _tx_execution_isr_exit
55    ENDIF
56;
57;
58        AREA ||.text||, CODE, READONLY
59        PRESERVE8
60;/**************************************************************************/
61;/*                                                                        */
62;/*  FUNCTION                                               RELEASE        */
63;/*                                                                        */
64;/*    _tx_thread_fiq_context_restore                      ARM11/AC5       */
65;/*                                                            6.1         */
66;/*  AUTHOR                                                                */
67;/*                                                                        */
68;/*    William E. Lamie, Microsoft Corporation                             */
69;/*                                                                        */
70;/*  DESCRIPTION                                                           */
71;/*                                                                        */
72;/*    This function restores the fiq interrupt context when processing a  */
73;/*    nested interrupt.  If not, it returns to the interrupt thread if no */
74;/*    preemption is necessary.  Otherwise, if preemption is necessary or  */
75;/*    if no thread was running, the function returns to the scheduler.    */
76;/*                                                                        */
77;/*  INPUT                                                                 */
78;/*                                                                        */
79;/*    None                                                                */
80;/*                                                                        */
81;/*  OUTPUT                                                                */
82;/*                                                                        */
83;/*    None                                                                */
84;/*                                                                        */
85;/*  CALLS                                                                 */
86;/*                                                                        */
87;/*    _tx_thread_schedule                   Thread scheduling routine     */
88;/*                                                                        */
89;/*  CALLED BY                                                             */
90;/*                                                                        */
91;/*    FIQ ISR                               Interrupt Service Routines    */
92;/*                                                                        */
93;/*  RELEASE HISTORY                                                       */
94;/*                                                                        */
95;/*    DATE              NAME                      DESCRIPTION             */
96;/*                                                                        */
97;/*  09-30-2020     William E. Lamie         Initial Version 6.1           */
98;/*                                                                        */
99;/**************************************************************************/
100;VOID   _tx_thread_fiq_context_restore(VOID)
101;{
102    EXPORT  _tx_thread_fiq_context_restore
103_tx_thread_fiq_context_restore
104;
105;    /* Lockout interrupts.  */
106;
107    MRS     r3, CPSR                            ; Pickup current CPSR
108    ORR     r0, r3, #DISABLE_INTS               ; Build interrupt disable value
109    MSR     CPSR_cxsf, r0                       ; Lockout interrupts
110
111    IF :DEF:TX_ENABLE_EXECUTION_CHANGE_NOTIFY
112;
113;    /* Call the ISR exit function to indicate an ISR is complete.  */
114;
115    BL      _tx_execution_isr_exit              ; Call the ISR exit function
116    ENDIF
117;
118;    /* Determine if interrupts are nested.  */
119;    if (--_tx_thread_system_state)
120;    {
121;
122    LDR     r3, =_tx_thread_system_state        ; Pickup address of system state var
123    LDR     r2, [r3]                            ; Pickup system state
124    SUB     r2, r2, #1                          ; Decrement the counter
125    STR     r2, [r3]                            ; Store the counter
126    CMP     r2, #0                              ; Was this the first interrupt?
127    BEQ     __tx_thread_fiq_not_nested_restore  ; If so, not a nested restore
128;
129;    /* Interrupts are nested.  */
130;
131;    /* Just recover the saved registers and return to the point of
132;       interrupt.  */
133;
134    LDMIA   sp!, {r0, r10, r12, lr}             ; Recover SPSR, POI, and scratch regs
135    MSR     SPSR_cxsf, r0                       ; Put SPSR back
136    LDMIA   sp!, {r0-r3}                        ; Recover r0-r3
137    MOVS    pc, lr                              ; Return to point of interrupt
138;
139;    }
140__tx_thread_fiq_not_nested_restore
141;
142;    /* Determine if a thread was interrupted and no preemption is required.  */
143;    else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr))
144;               || (_tx_thread_preempt_disable))
145;    {
146;
147    LDR     r1, [sp]                            ; Pickup the saved SPSR
148    MOV     r2, #MODE_MASK                      ; Build mask to isolate the interrupted mode
149    AND     r1, r1, r2                          ; Isolate mode bits
150    CMP     r1, #IRQ_MODE_BITS                  ; Was an interrupt taken in IRQ mode before we
151                                                ;   got to context save? */
152    BEQ     __tx_thread_fiq_no_preempt_restore  ; Yes, just go back to point of interrupt
153
154
155    LDR     r1, =_tx_thread_current_ptr         ; Pickup address of current thread ptr
156    LDR     r0, [r1]                            ; Pickup actual current thread pointer
157    CMP     r0, #0                              ; Is it NULL?
158    BEQ     __tx_thread_fiq_idle_system_restore ; Yes, idle system was interrupted
159
160    LDR     r3, =_tx_thread_preempt_disable     ; Pickup preempt disable address
161    LDR     r2, [r3]                            ; Pickup actual preempt disable flag
162    CMP     r2, #0                              ; Is it set?
163    BNE     __tx_thread_fiq_no_preempt_restore  ; Yes, don't preempt this thread
164    LDR     r3, =_tx_thread_execute_ptr         ; Pickup address of execute thread ptr
165    LDR     r2, [r3]                            ; Pickup actual execute thread pointer
166    CMP     r0, r2                              ; Is the same thread highest priority?
167    BNE     __tx_thread_fiq_preempt_restore     ; No, preemption needs to happen
168
169
170__tx_thread_fiq_no_preempt_restore
171;
172;    /* Restore interrupted thread or ISR.  */
173;
174;    /* Pickup the saved stack pointer.  */
175;    tmp_ptr =  _tx_thread_current_ptr -> tx_thread_stack_ptr;
176;
177;    /* Recover the saved context and return to the point of interrupt.  */
178;
179    LDMIA   sp!, {r0, lr}                       ; Recover SPSR, POI, and scratch regs
180    MSR     SPSR_cxsf, r0                       ; Put SPSR back
181    LDMIA   sp!, {r0-r3}                        ; Recover r0-r3
182    MOVS    pc, lr                              ; Return to point of interrupt
183;
184;    }
185;    else
186;    {
187__tx_thread_fiq_preempt_restore
188;
189    LDMIA   sp!, {r3, lr}                       ; Recover temporarily saved registers
190    MOV     r1, lr                              ; Save lr (point of interrupt)
191    MOV     r2, #SVC_MODE                       ; Build SVC mode CPSR
192    MSR     CPSR_cxsf, r2                       ; Enter SVC mode
193    STR     r1, [sp, #-4]!                      ; Save point of interrupt
194    STMDB   sp!, {r4-r12, lr}                   ; Save upper half of registers
195    MOV     r4, r3                              ; Save SPSR in r4
196    MOV     r2, #FIQ_MODE                       ; Build FIQ mode CPSR
197    MSR     CPSR_cxsf, r2                       ; Re-enter FIQ mode
198    LDMIA   sp!, {r0-r3}                        ; Recover r0-r3
199    MOV     r5, #SVC_MODE                       ; Build SVC mode CPSR
200    MSR     CPSR_cxsf, r5                       ; Enter SVC mode
201    STMDB   sp!, {r0-r3}                        ; Save r0-r3 on thread's stack
202    MOV     r3, #1                              ; Build interrupt stack type
203    STMDB   sp!, {r3, r4}                       ; Save interrupt stack type and SPSR
204    LDR     r1, =_tx_thread_current_ptr         ; Pickup address of current thread ptr
205    LDR     r0, [r1]                            ; Pickup current thread pointer
206    STR     sp, [r0, #8]                        ; Save stack pointer in thread control
207                                                ;   block  */
208    BIC     r4, r4, #THUMB_MASK                 ; Clear the Thumb bit of CPSR
209    ORR     r3, r4, #DISABLE_INTS               ; Or-in interrupt lockout bit(s)
210    MSR     CPSR_cxsf, r3                       ; Lockout interrupts
211;
212;    /* Save the remaining time-slice and disable it.  */
213;    if (_tx_timer_time_slice)
214;    {
215;
216    LDR     r3, =_tx_timer_time_slice           ; Pickup time-slice variable address
217    LDR     r2, [r3]                            ; Pickup time-slice
218    CMP     r2, #0                              ; Is it active?
219    BEQ     __tx_thread_fiq_dont_save_ts        ; No, don't save it
220;
221;        _tx_thread_current_ptr -> tx_thread_time_slice =  _tx_timer_time_slice;
222;        _tx_timer_time_slice =  0;
223;
224    STR     r2, [r0, #24]                       ; Save thread's time-slice
225    MOV     r2, #0                              ; Clear value
226    STR     r2, [r3]                            ; Disable global time-slice flag
227;
228;    }
229__tx_thread_fiq_dont_save_ts
230;
231;
232;    /* Clear the current task pointer.  */
233;    _tx_thread_current_ptr =  TX_NULL;
234;
235    MOV     r0, #0                              ; NULL value
236    STR     r0, [r1]                            ; Clear current thread pointer
237;
238;    /* Return to the scheduler.  */
239;    _tx_thread_schedule();
240;
241    B       _tx_thread_schedule                 ; Return to scheduler
242;    }
243;
244__tx_thread_fiq_idle_system_restore
245;
246;    /* Just return back to the scheduler!  */
247;
248    ADD     sp, sp, #24                         ; Recover FIQ stack space
249    MRS     r3, CPSR                            ; Pickup current CPSR
250    BIC     r3, r3, #MODE_MASK                  ; Clear the mode portion of the CPSR
251    ORR     r3, r3, #SVC_MODE_BITS              ; Or-in new interrupt lockout bit
252    MSR     CPSR_cxsf, r3                       ; Lockout interrupts
253    B       _tx_thread_schedule                 ; Return to scheduler
254;
255;}
256;
257    END
258
259