move new files out from platform support patch
[openwrt/staging/yousong.git] / target / linux / ubicom32 / files / arch / ubicom32 / kernel / ubicom32_context_switch.S
1 /*
2 * arch/ubicom32/kernel/ubicom32_context_switch.S
3 * Implements context switch and return functions.
4 *
5 * (C) Copyright 2009, Ubicom, Inc.
6 *
7 * This file is part of the Ubicom32 Linux Kernel Port.
8 *
9 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
10 * it and/or modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation, either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * The Ubicom32 Linux Kernel Port is distributed in the hope that it
15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with the Ubicom32 Linux Kernel Port. If not,
21 * see <http://www.gnu.org/licenses/>.
22 *
23 * Ubicom32 implementation derived from (with many thanks):
24 * arch/m68knommu
25 * arch/blackfin
26 * arch/parisc
27 */
28 #include <linux/sys.h>
29 #include <linux/linkage.h>
30 #include <asm/asm-offsets.h>
31 #include <asm/ubicom32-common.h>
32 #include <asm/ip5000.h>
33 #include <asm/range-protect.h>
34
35 /*
36 * begin_restore_context()
37 * Restore most of the context from sp (struct pt_reg *)
38 *
39 * This *can* be called without the global atomic lock. (because sp is
40 * not restored!) Only d15 and a3 are allowed to be used after this
41 * before calling complete_restore_context
42 */
43 .macro begin_restore_context
44 move.4 d0, PT_D0(sp)
45 move.4 d1, PT_D1(sp)
46 move.4 d2, PT_D2(sp)
47 move.4 d3, PT_D3(sp)
48 move.4 d4, PT_D4(sp)
49 move.4 d5, PT_D5(sp)
50 move.4 d6, PT_D6(sp)
51 move.4 d7, PT_D7(sp)
52 move.4 d8, PT_D8(sp)
53 move.4 d9, PT_D9(sp)
54 move.4 d10, PT_D10(sp)
55 move.4 d11, PT_D11(sp)
56 move.4 d12, PT_D12(sp)
57 move.4 d13, PT_D13(sp)
58 move.4 d14, PT_D14(sp)
59 ;; move.4 d15, PT_D15(sp)
60 move.4 a0, PT_A0(sp)
61 move.4 a1, PT_A1(sp)
62 move.4 a2, PT_A2(sp)
63 ;; move.4 a3, PT_A3(sp)
64 move.4 a4, PT_A4(sp)
65 move.4 a5, PT_A5(sp)
66 move.4 a6, PT_A6(sp)
67 move.4 acc0_hi, PT_ACC0HI(sp)
68 move.4 acc0_lo, PT_ACC0LO(sp)
69 move.4 mac_rc16, PT_MAC_RC16(sp)
70 move.4 acc1_hi, PT_ACC1HI(sp)
71 move.4 acc1_lo, PT_ACC1LO(sp)
72 move.4 source3, PT_SOURCE3(sp)
73 move.4 int_mask0, PT_INT_MASK0(sp)
74 move.4 int_mask1, PT_INT_MASK1(sp)
75 .endm
76
77 /*
78 * complete_restore_context()
79 * Completely restore the context from sp (struct pt_reg *)
80 *
81 * Note: Recovered PC and CSR are saved on the stack and are to be
82 * popped off before returning.
83 */
84 .macro complete_restore_context
85 move.4 a3, sp
86 move.4 d15, PT_D15(sp)
87 move.4 sp, PT_SP(a3) ; Recover Stack pointer from save area
88 move.4 -4(sp)++, PT_PC(a3) ; Recover saved PC and save to stack
89 move.4 -4(sp)++, PT_CSR(a3) ; Recover saved csr and save to stack
90 move.4 a3, PT_A3(a3)
91 .endm
92
93 /*
94 * old restore_context macro
95 */
96 .macro restore_context
97 begin_restore_context
98 complete_restore_context
99 .endm
100
101 /*
102 * ldsr_thread_enable_interrupts()
103 * An assembly version of the enable interrupts function.
104 *
105 * The stack is fair game but all registers MUST be preserved.
106 *
107 */
108 .macro ldsr_thread_enable_interrupts
109 move.4 -4(sp)++, d3 ; Push d3
110 move.4 -4(sp)++, a3 ; Push a3
111
112 /*
113 * Read the ROSR and obtain ~(1 << tid)
114 */
115 lsr.4 d3, rosr, #0x2 ; Move the thread portion of ROSR into d3
116 lsl.4 d3, #1, d3 ; perform a (1 << tid)
117 not.4 d3, d3 ; Negate the value of d3 == ~(1 << threadid)
118
119 /*
120 * Get the value of the ldsr_soft_irq_mask
121 */
122 moveai a3, #%hi(ldsr_soft_irq_mask)
123 move.4 a3, %lo(ldsr_soft_irq_mask)(a3)
124
125 /*
126 * Now re-enable interrupts for this thread and then
127 * wakeup the LDSR.
128 */
129 and.4 scratchpad1, scratchpad1, d3
130 move.4 int_set0, a3
131
132 /*
133 * Restore the registers.
134 */
135 move.4 a3, (sp)4++
136 move.4 d3, (sp)4++
137 .endm
138
139 /*
140 * ret_from_interrupt_to_kernel()
141 * RFI function that is where do_IRQ() returns to if the thread was
142 * in kernel space.
143 */
144 .section .text.ret_from_interrupt_to_kernel, "ax", @progbits
145 .global ret_from_interrupt_to_kernel
146 ret_from_interrupt_to_kernel:
147 begin_restore_context ; Restore the thread context
148 atomic_lock_acquire ; Enter critical section
149 complete_restore_context ; Restore the thread context
150 atomic_lock_release ; Leave critical section
151 ldsr_thread_enable_interrupts ; enable the threads interrupts
152 move.4 csr, (sp)4++ ; Restore csr from the stack
153 ret (sp)4++
154
155 /*
156 * ret_from_interrupt_to_user()
157 * RFI function that is where do_IRQ() returns to if the thread was
158 * in user space.
159 *
160 * TODO: Do we really need the critical section handling in this code?
161 *
162 */
163 .section .text.ret_from_interrupt_to_user, "ax", @progbits
164 .global ret_from_interrupt_to_user
165 ret_from_interrupt_to_user:
166 ldsr_thread_enable_interrupts ; enable the threads interrupts
167 /*
168 * Set a1 to the thread info pointer, no need to save it as we are
169 * restoring userspace and will never return
170 */
171 movei d0, #(~(ASM_THREAD_SIZE-1))
172 and.4 a1, sp, d0
173
174 /*
175 * Test if the scheduler needs to be called.
176 */
177 btst TI_FLAGS(a1), #ASM_TIF_NEED_RESCHED
178 jmpeq.t 2f
179 call a5, schedule ; Call the scheduler. I will come back here.
180
181 /*
182 * See if we have pending signals and call do_signal
183 * if needed.
184 */
185 2:
186 btst TI_FLAGS(a1), #ASM_TIF_SIGPENDING ; Any signals needed?
187 jmpeq.t 1f
188
189 /*
190 * Now call do_signal()
191 */
192 move.4 d0, #0 ; oldset pointer is NULL
193 move.4 d1, sp ; d1 is the regs pointer
194 call a5, do_signal ; Call do_signal()
195
196 /*
197 * Back from do_signal(), re-enter critical section.
198 */
199 1:
200 begin_restore_context ; Restore the thread context
201 atomic_lock_acquire ; Enter critical section
202 call a3, __complete_and_return_to_userspace ; jump to unprotected section
203
204 /*
205 * restore_all_registers()
206 *
207 * restore_all_registers will be the alternate exit route for
208 * preempted processes that have called a signal handler
209 * and are returning back to user space.
210 */
211 .section .text.restore_all_registers, "ax", @progbits
212 .global restore_all_registers
213 restore_all_registers:
214 begin_restore_context ; Restore the thread context
215 atomic_lock_acquire ; Enter critical section
216 call a3, __complete_and_return_to_userspace
217
218 /*
219 * __complete_and_return_to_userspace
220 *
221 * restores the second half of the context and returns
222 * You must have the atomic lock when you call this function
223 */
224 .section .kernel_unprotected, "ax", @progbits
225 __complete_and_return_to_userspace:
226 disable_kernel_ranges_for_current d15 ; disable kernel ranges
227 complete_restore_context ; restore previous context
228 atomic_lock_release ; Leave critical section
229 move.4 csr, (sp)4++ ; Restore csr from the stack
230 ret (sp)4++
231
232 /*
233 * ret_from_fork()
234 * Called on the child's return from fork system call.
235 */
236 .section .text.ret_from_fork, "ax", @progbits
237 .global ret_from_fork
238 ret_from_fork:
239 ;;; d0 contains the arg for schedule_tail
240 ;;; the others we don't care about as they are in PT_REGS (sp)
241 call a5, schedule_tail
242
243 atomic_lock_acquire ; Enter critical section
244
245 move.4 a3, sp
246 move.4 d0, PT_D0(a3) ; Restore D0
247 move.4 d1, PT_D1(a3) ; Restore D1
248 move.4 d2, PT_D2(a3) ; Restore D2
249 move.4 d3, PT_D3(a3) ; Restore D3
250 move.4 d10, PT_D10(a3) ; Restore D10
251 move.4 d11, PT_D11(a3) ; Restore D11
252 move.4 d12, PT_D12(a3) ; Restore D12
253 move.4 d13, PT_D13(a3) ; Restore D13
254 move.4 a1, PT_A1(a3) ; Restore A1
255 move.4 a2, PT_A2(a3) ; Restore A2
256 move.4 a5, PT_A5(a3) ; Restore A5
257 move.4 a6, PT_A6(a3) ; Restore A6
258 ;; I think atomic_lock_acquire could be moved here..
259 move.4 sp, PT_SP(a3) ; Restore sp
260 move.4 a4, PT_PC(a3) ; Restore pc in register a4
261 move.4 PT_FRAME_TYPE(a3), #0 ; Clear frame_type to indicate it is invalid.
262
263 #ifdef CONFIG_PROTECT_KERNEL
264 call a3, __ret_from_fork_bottom_half
265 .section .kernel_unprotected, "ax", @progbits
266 __ret_from_fork_bottom_half:
267 disable_kernel_ranges_for_current d15
268 #endif
269 atomic_lock_release ; Leave critical section
270 calli a4, 0(a4) ; Return.
271
272 /*
273 * __switch_to()
274 *
275 * Call with:
276 * void *__switch_to(struct task_struct *prev, struct thread_struct *prev_switch,
277 * struct thread_struct *next_switch)
278 */
279 .section .text.__switch_to, "ax", @progbits
280 .global __switch_to
281 __switch_to:
282
283 /*
284 * Set up register a3 to point to save area.
285 */
286 movea a3, d1 ; a3 now holds prev_switch
287 move.4 (a3)4++, d10
288 move.4 (a3)4++, d11
289 move.4 (a3)4++, d12
290 move.4 (a3)4++, d13
291 move.4 (a3)4++, a1
292 move.4 (a3)4++, a2
293 move.4 (a3)4++, a5
294 move.4 (a3)4++, a6
295 move.4 (a3)4++, a7
296
297 /*
298 * Set up register a3 to point to restore area.
299 */
300 movea a3, d2 ; a3 now holds next_switch
301 move.4 d10 , (a3)4++
302 move.4 d11 , (a3)4++
303 move.4 d12 , (a3)4++
304 move.4 d13 , (a3)4++
305 move.4 a1 , (a3)4++
306 move.4 a2 , (a3)4++
307 move.4 a5 , (a3)4++
308 move.4 a6 , (a3)4++
309 move.4 a7 , (a3)4++
310
311 /*
312 * Load the sw_ksp with the proper thread_info pointer.
313 */
314 movei d15, #(~(ASM_THREAD_SIZE-1))
315 and.4 a3, sp, d15 ; a3 now has the thread info pointer
316 moveai a4, #%hi(sw_ksp)
317 lea.1 a4, %lo(sw_ksp)(a4) ; a4 now has the base address of sw_ksp array
318 lsr.4 d15, ROSR, #2 ; Thread number - bit's 6 through 31 are zeroes anyway.
319 move.4 (a4, d15), a3 ; Load the thread info pointer into the hw_ksp array..
320
321 /*
322 * We are done with context switch. Time to return..
323 */
324 calli a5, 0(a5)
325 .size __switch_to, . - __switch_to
326
327 /*
328 * ubicom32_emulate_insn()
329 * Emulates the instruction.
330 *
331 * Call with:
332 * unsigned int ubicom32_emulate_insn(int source1, int source2, int source3, int *save_acc, int *save_csr);
333 */
334 .section .text.ubicom32_emulate_insn, "ax", @progbits
335 .global ubicom32_emulate_insn
336 .global trap_emulate
337 ubicom32_emulate_insn:
338 movea a3, d3 ; a3 holds save_acc pointer
339 movea a4, d4 ; a4 hods save_csr pointer
340 move.4 source3, d2
341 move.4 acc0_lo, (a3)
342 move.4 acc0_hi, 4(a3)
343 move.4 acc1_lo, 8(a3)
344 move.4 acc1_hi, 12(a3)
345 move.4 mac_rc16, 16(a3)
346 move.4 CSR, (a4)
347 setcsr_flush 0
348
349 trap_emulate:
350 move.4 d0, d1
351 setcsr_flush 0
352 move.4 (a4), CSR ; Save csr
353 move.4 (a3), acc0_lo
354 move.4 4(a3), acc0_hi
355 move.4 8(a3), acc1_lo
356 move.4 12(a3), acc1_hi
357 move.4 16(a3), mac_rc16
358 ret a5
359 .size ubicom32_emulate_insn, . - ubicom32_emulate_insn