author | William Roberts <williamr@symbian.org> |
Fri, 11 Jun 2010 17:56:35 +0100 | |
branch | GCC_SURGE |
changeset 144 | c5e01f2a4bfd |
parent 0 | a41df078684a |
permissions | -rw-r--r-- |
0 | 1 |
// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies). |
2 |
// All rights reserved. |
|
3 |
// This component and the accompanying materials are made available |
|
4 |
// under the terms of the License "Eclipse Public License v1.0" |
|
5 |
// which accompanies this distribution, and is available |
|
6 |
// at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 |
// |
|
8 |
// Initial Contributors: |
|
9 |
// Nokia Corporation - initial contribution. |
|
10 |
// |
|
11 |
// Contributors: |
|
12 |
// |
|
13 |
// Description: |
|
14 |
// e32\nkern\arm\ncutilf.cia |
|
15 |
// |
|
16 |
// |
|
17 |
||
18 |
#include <e32cia.h> |
|
19 |
#include <arm.h> |
|
20 |
#include "highrestimer.h" |
|
21 |
||
22 |
#ifdef __SCHEDULER_MACHINE_CODED__ |
|
23 |
/** Signals the request semaphore of a nanothread. |
|
24 |
||
25 |
This function is intended to be used by the EPOC layer and personality |
|
26 |
layers. Device drivers should use Kern::RequestComplete instead. |
|
27 |
||
28 |
@param aThread Nanothread to signal. Must be non NULL. |
|
29 |
||
30 |
@see Kern::RequestComplete() |
|
31 |
||
32 |
@pre Interrupts must be enabled. |
|
33 |
@pre Do not call from an ISR. |
|
34 |
*/ |
|
35 |
EXPORT_C __NAKED__ void NKern::ThreadRequestSignal(NThread* /*aThread*/) |
|
36 |
{ |
|
37 |
ASM_CHECK_PRECONDITIONS(MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR); |
|
38 |
||
39 |
asm("ldr r2, __TheScheduler "); |
|
40 |
asm("str lr, [sp, #-4]! "); |
|
41 |
asm("ldr r3, [r2, #%a0]" : : "i" _FOFF(TScheduler,iKernCSLocked)); |
|
42 |
asm("add r0, r0, #%a0" : : "i" _FOFF(NThread,iRequestSemaphore)); |
|
43 |
asm("add r3, r3, #1 "); |
|
44 |
asm("str r3, [r2, #%a0]" : : "i" _FOFF(TScheduler,iKernCSLocked)); |
|
45 |
asm("bl " CSM_ZN14NFastSemaphore6SignalEv); // alignment OK since target is also assembler |
|
46 |
asm("ldr lr, [sp], #4 "); |
|
47 |
asm("b " CSM_ZN5NKern6UnlockEv); |
|
48 |
} |
|
49 |
||
50 |
||
51 |
/** Atomically signals the request semaphore of a nanothread and a fast mutex. |
|
52 |
||
53 |
This function is intended to be used by the EPOC layer and personality |
|
54 |
layers. Device drivers should use Kern::RequestComplete instead. |
|
55 |
||
56 |
@param aThread Nanothread to signal. Must be non NULL. |
|
57 |
@param aMutex Fast mutex to signal. If NULL, the system lock is signaled. |
|
58 |
||
59 |
@see Kern::RequestComplete() |
|
60 |
||
61 |
@pre Kernel must be unlocked. |
|
62 |
@pre Call in a thread context. |
|
63 |
@pre Interrupts must be enabled. |
|
64 |
*/ |
|
65 |
EXPORT_C __NAKED__ void NKern::ThreadRequestSignal(NThread* /*aThread*/, NFastMutex* /*aMutex*/) |
|
66 |
{ |
|
67 |
ASM_CHECK_PRECONDITIONS(MASK_INTERRUPTS_ENABLED|MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC); |
|
68 |
||
69 |
asm("ldr r2, __TheScheduler "); |
|
70 |
asm("cmp r1, #0 "); |
|
71 |
asm("ldreq r1, __SystemLock "); |
|
72 |
asm("ldr r3, [r2, #%a0]" : : "i" _FOFF(TScheduler,iKernCSLocked)); |
|
73 |
asm("stmfd sp!, {r1,lr} "); |
|
74 |
asm("add r0, r0, #%a0" : : "i" _FOFF(NThread,iRequestSemaphore)); |
|
75 |
asm("add r3, r3, #1 "); |
|
76 |
asm("str r3, [r2, #%a0]" : : "i" _FOFF(TScheduler,iKernCSLocked)); |
|
77 |
asm("bl " CSM_ZN14NFastSemaphore6SignalEv); |
|
78 |
asm("ldr r0, [sp], #4 "); |
|
79 |
asm("bl " CSM_ZN10NFastMutex6SignalEv); // alignment OK since target is also assembler |
|
80 |
asm("ldr lr, [sp], #4 "); |
|
81 |
asm("b " CSM_ZN5NKern6UnlockEv); |
|
82 |
||
83 |
asm("__SystemLock: "); |
|
144
c5e01f2a4bfd
Remove the (TInt) casts from address expressions in asm() statements, as a fix for Bug 2905
William Roberts <williamr@symbian.org>
parents:
0
diff
changeset
|
84 |
asm(".word %a0" : : "i" (&TheScheduler.iLock)); |
0 | 85 |
asm("__TheScheduler: "); |
86 |
asm(".word TheScheduler "); |
|
87 |
} |
|
88 |
#endif |
|
89 |
||
90 |
||
91 |
#ifndef __USER_CONTEXT_TYPE_MACHINE_CODED__ |
|
92 |
// called by C++ version of NThread::UserContextType() |
|
93 |
__NAKED__ TBool RescheduledAfterInterrupt(TUint32 /*aAddr*/) |
|
94 |
{ |
|
95 |
asm("ldr r1, __irq_resched_return "); |
|
96 |
asm("cmp r0, r1 "); |
|
97 |
asm("movne r0, #0 "); |
|
98 |
__JUMP(,lr); |
|
99 |
asm("__irq_resched_return: "); |
|
100 |
asm(".word irq_resched_return "); |
|
101 |
} |
|
102 |
||
103 |
#else |
|
104 |
||
105 |
/** Get a value which indicates where a thread's user mode context is stored. |
|
106 |
||
107 |
@return A value that can be used as an index into the tables returned by |
|
108 |
NThread::UserContextTables(). |
|
109 |
||
110 |
@pre any context |
|
111 |
@pre kernel locked |
|
112 |
@post kernel locked |
|
113 |
||
114 |
@see UserContextTables |
|
115 |
@publishedPartner |
|
116 |
*/ |
|
117 |
EXPORT_C __NAKED__ NThread::TUserContextType NThread::UserContextType() |
|
118 |
{ |
|
119 |
ASM_CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR); |
|
120 |
// |
|
121 |
// Optimisation note: It may be possible to coalesce the first and second |
|
122 |
// checks below by creating separate "EContextXxxDied" context types for each |
|
123 |
// possible way a thread can die and ordering these new types before |
|
124 |
// EContextException. |
|
125 |
// |
|
126 |
||
127 |
// Dying thread? use context saved earlier by kernel |
|
128 |
||
129 |
asm("ldr r3, [r0, #%a0]" : : "i" _FOFF(NThread,iCsFunction)); |
|
130 |
asm("ldrb r2, [r0, #%a0]" : : "i" _FOFF(NThread,iSpare3)); // r2 = iUserContextType |
|
131 |
asm("mov r1, r0 "); // r1 = this |
|
132 |
asm("cmp r3, #%a0" : : "i" ((TInt)NThread::ECSExitInProgress)); |
|
133 |
asm("moveq r0, r2"); |
|
134 |
__JUMP(eq,lr); |
|
135 |
||
136 |
// Exception or no user context? |
|
137 |
||
138 |
asm("ldr r3, __TheScheduler"); |
|
139 |
asm("cmp r2, #%a0 " : : "i" ((TInt)NThread::EContextException)); |
|
140 |
asm("ldr r3, [r3, #%a0]" : : "i" _FOFF(TScheduler,iCurrentThread)); |
|
141 |
asm("movls r0, r2 "); // Return EContextNone or EContextException |
|
142 |
__JUMP(ls,lr); |
|
143 |
asm("cmp r2, #%a0 " : : "i" ((TInt)NThread::EContextUserIntrCallback)); |
|
144 |
asm("blo 1f"); |
|
145 |
asm("cmp r2, #%a0 " : : "i" ((TInt)NThread::EContextWFARCallback)); |
|
146 |
asm("movls r0, r2 "); // Return EContextUserIntrCallback or EContextWFARCallback |
|
147 |
__JUMP(ls,lr); |
|
148 |
||
149 |
// Getting current thread context? must be in exec call as exception |
|
150 |
// and dying thread cases were tested above. |
|
151 |
||
152 |
asm("1: "); |
|
153 |
asm("cmp r3, r1"); |
|
154 |
asm("moveq r0, #%a0" : : "i" ((TInt)NThread::EContextExec)); |
|
155 |
__JUMP(eq,lr); |
|
156 |
||
157 |
asm("ldr r0, [r1, #%a0]" : : "i" _FOFF(NThread,iStackBase)); |
|
158 |
asm("ldr r2, [r1, #%a0]" : : "i" _FOFF(NThread,iStackSize)); |
|
159 |
asm("ldr r3, [r1, #%a0]" : : "i" _FOFF(NThread,iSavedSP)); |
|
160 |
asm("add r2, r2, r0"); |
|
161 |
asm("ldr r0, [r3, #%a0]" : : "i" (EXTRA_STACK_SPACE+11*4)); // get saved return address from reschedule |
|
162 |
asm("ldr r12, __irq_resched_return "); |
|
163 |
asm("sub r2, r2, r3"); |
|
164 |
asm("cmp r0, r12 "); |
|
165 |
asm("beq preempted "); |
|
166 |
||
167 |
// Transition to supervisor mode must have been due to a SWI |
|
168 |
||
169 |
asm("not_preempted:"); |
|
170 |
asm("cmp r2, #%a0 " : : "i" ((TInt)(EXTRA_STACK_SPACE+15*4))); |
|
171 |
asm("moveq r0, #%a0 " : : "i" ((TInt)NThread::EContextWFAR)); // thread must have blocked doing Exec::WaitForAnyRequest |
|
172 |
asm("movne r0, #%a0 " : : "i" ((TInt)NThread::EContextExec)); // Thread must have been in a SLOW or UNPROTECTED Exec call |
|
173 |
__JUMP(,lr); |
|
174 |
||
175 |
// thread was preempted due to an interrupt |
|
176 |
// interrupt and reschedule will have pushed ? words + USER_MEMORY_GUARD_SAVE_WORDS + EXTRA_STACK_SPACE onto the stack |
|
177 |
||
178 |
asm("preempted:"); |
|
179 |
asm("ldr r12, [r3, #%a0]" : : "i" (EXTRA_STACK_SPACE+USER_MEMORY_GUARD_SAVE_WORDS*4+12*4)); // first word on stack before reschedule |
|
180 |
asm("mov r0, #%a0 " : : "i" ((TInt)NThread::EContextUserInterrupt)); |
|
181 |
asm("and r12, r12, #0x1f "); |
|
182 |
asm("cmp r12, #0x10 "); // interrupted mode = user? |
|
183 |
__JUMP(eq,lr); |
|
184 |
||
185 |
asm("cmp r2, #%a0 " : : "i" ((TInt)(EXTRA_STACK_SPACE+USER_MEMORY_GUARD_SAVE_WORDS*4+30*4))); |
|
186 |
asm("bcs not_preempted "); // thread was interrupted in supervisor mode, return address and r4-r11 were saved |
|
187 |
||
188 |
// interrupt occurred in exec call entry before r4-r11 saved |
|
189 |
asm("cmp r2, #%a0 " : : "i" ((TInt)(EXTRA_STACK_SPACE+USER_MEMORY_GUARD_SAVE_WORDS*4+20*4))); |
|
190 |
asm("moveq r0, #%a0 " : : "i" ((TInt)NThread::EContextSvsrInterrupt1)); // interrupt before return address was saved or after registers restored |
|
191 |
asm("movne r0, #%a0 " : : "i" ((TInt)NThread::EContextSvsrInterrupt2)); // interrupt after return address saved |
|
192 |
__JUMP(,lr); |
|
193 |
||
194 |
asm("__irq_resched_return: "); |
|
195 |
asm(".word irq_resched_return "); |
|
196 |
} |
|
197 |
||
198 |
#endif // __USER_CONTEXT_TYPE_MACHINE_CODED__ |
|
199 |
||
200 |
__NAKED__ void Arm::GetUserSpAndLr(TAny*) |
|
201 |
{ |
|
202 |
asm("stmia r0, {r13, r14}^ "); |
|
203 |
asm("mov r0, r0"); // NOP needed between stm^ and banked register access |
|
204 |
__JUMP(,lr); |
|
205 |
} |
|
206 |
||
207 |
__NAKED__ void Arm::SetUserSpAndLr(TAny*) |
|
208 |
{ |
|
209 |
asm("ldmia r0, {r13, r14}^ "); |
|
210 |
asm("mov r0, r0"); // NOP needed between ldm^ and banked register access |
|
211 |
__JUMP(,lr); |
|
212 |
} |
|
213 |
||
214 |
#ifdef __CPU_ARM_USE_DOMAINS |
|
215 |
__NAKED__ TUint32 Arm::Dacr() |
|
216 |
{ |
|
217 |
asm("mrc p15, 0, r0, c3, c0, 0 "); |
|
218 |
__JUMP(,lr); |
|
219 |
} |
|
220 |
||
221 |
__NAKED__ void Arm::SetDacr(TUint32) |
|
222 |
{ |
|
223 |
asm("mcr p15, 0, r0, c3, c0, 0 "); |
|
224 |
CPWAIT(,r0); |
|
225 |
__JUMP(,lr); |
|
226 |
} |
|
227 |
||
228 |
__NAKED__ TUint32 Arm::ModifyDacr(TUint32, TUint32) |
|
229 |
{ |
|
230 |
asm("mrc p15, 0, r2, c3, c0, 0 "); |
|
231 |
asm("bic r2, r2, r0 "); |
|
232 |
asm("orr r2, r2, r1 "); |
|
233 |
asm("mcr p15, 0, r2, c3, c0, 0 "); |
|
234 |
CPWAIT(,r0); |
|
235 |
asm("mov r0, r2 "); |
|
236 |
__JUMP(,lr); |
|
237 |
} |
|
238 |
#endif |
|
239 |
||
240 |
#ifdef __CPU_HAS_COPROCESSOR_ACCESS_REG |
|
241 |
__NAKED__ void Arm::SetCar(TUint32) |
|
242 |
{ |
|
243 |
SET_CAR(,r0); |
|
244 |
CPWAIT(,r0); |
|
245 |
__JUMP(,lr); |
|
246 |
} |
|
247 |
#endif |
|
248 |
||
249 |
||
250 |
||
251 |
/** Get the CPU's coprocessor access register value |
|
252 |
||
253 |
@return The value of the CAR, 0 if CPU doesn't have CAR |
|
254 |
||
255 |
@publishedPartner |
|
256 |
@released |
|
257 |
*/ |
|
258 |
EXPORT_C __NAKED__ TUint32 Arm::Car() |
|
259 |
{ |
|
260 |
#ifdef __CPU_HAS_COPROCESSOR_ACCESS_REG |
|
261 |
GET_CAR(,r0); |
|
262 |
#else |
|
263 |
asm("mov r0, #0 "); |
|
264 |
#endif |
|
265 |
__JUMP(,lr); |
|
266 |
} |
|
267 |
||
268 |
||
269 |
||
270 |
/** Modify the CPU's coprocessor access register value |
|
271 |
Does nothing if CPU does not have CAR. |
|
272 |
||
273 |
@param aClearMask Mask of bits to clear (1 = clear this bit) |
|
274 |
@param aSetMask Mask of bits to set (1 = set this bit) |
|
275 |
@return The original value of the CAR, 0 if CPU doesn't have CAR |
|
276 |
||
277 |
@publishedPartner |
|
278 |
@released |
|
279 |
*/ |
|
280 |
EXPORT_C __NAKED__ TUint32 Arm::ModifyCar(TUint32 /*aClearMask*/, TUint32 /*aSetMask*/) |
|
281 |
{ |
|
282 |
#ifdef __CPU_HAS_COPROCESSOR_ACCESS_REG |
|
283 |
GET_CAR(,r2); |
|
284 |
asm("bic r0, r2, r0 "); |
|
285 |
asm("orr r0, r0, r1 "); |
|
286 |
SET_CAR(,r0); |
|
287 |
CPWAIT(,r0); |
|
288 |
asm("mov r0, r2 "); |
|
289 |
#else |
|
290 |
asm("mov r0, #0 "); |
|
291 |
#endif |
|
292 |
__JUMP(,lr); |
|
293 |
} |
|
294 |
||
295 |
#ifdef __CPU_HAS_VFP |
|
296 |
__NAKED__ void Arm::SetFpExc(TUint32) |
|
297 |
{ |
|
298 |
#if defined(__CPU_ARM1136__) && !defined(__CPU_ARM1136_ERRATUM_351912_FIXED) |
|
299 |
// If we are about to enable VFP, disable dynamic branch prediction |
|
300 |
// If we are about to disable VFP, enable dynamic branch prediction if return stack prediction is enabled |
|
301 |
asm("mrs r3, cpsr "); |
|
302 |
CPSIDAIF; |
|
303 |
asm("mrc p15, 0, r1, c1, c0, 1 "); |
|
304 |
asm("tst r0, #%a0" : : "i" ((TInt)VFP_FPEXC_EN) ); |
|
305 |
asm("bic r1, r1, #2 "); // clear DB bit (disable dynamic prediction) |
|
306 |
asm("and r2, r1, #1 "); // r2 bit 0 = RS bit (1 if return stack enabled) |
|
307 |
asm("orreq r1, r1, r2, lsl #1 "); // if VFP is being disabled set DB = RS |
|
308 |
asm("mcr p15, 0, r1, c1, c0, 1 "); |
|
309 |
asm("mcr p15, 0, r2, c7, c5, 6 "); // flush BTAC |
|
310 |
VFP_FMXR(,VFP_XREG_FPEXC,0); |
|
311 |
asm("msr cpsr, r3 "); |
|
312 |
__JUMP(,lr); |
|
313 |
#else |
|
314 |
VFP_FMXR(,VFP_XREG_FPEXC,0); |
|
315 |
__JUMP(,lr); |
|
316 |
#endif |
|
317 |
} |
|
318 |
#endif |
|
319 |
||
320 |
||
321 |
||
322 |
/** Get the value of the VFP FPEXC register |
|
323 |
||
324 |
@return The value of FPEXC, 0 if there is no VFP |
|
325 |
||
326 |
@publishedPartner |
|
327 |
@released |
|
328 |
*/ |
|
329 |
EXPORT_C __NAKED__ TUint32 Arm::FpExc() |
|
330 |
{ |
|
331 |
#ifdef __CPU_HAS_VFP |
|
332 |
VFP_FMRX(,0,VFP_XREG_FPEXC); |
|
333 |
#else |
|
334 |
asm("mov r0, #0 "); |
|
335 |
#endif |
|
336 |
__JUMP(,lr); |
|
337 |
} |
|
338 |
||
339 |
||
340 |
||
341 |
/** Modify the VFP FPEXC register |
|
342 |
Does nothing if there is no VFP |
|
343 |
||
344 |
@param aClearMask Mask of bits to clear (1 = clear this bit) |
|
345 |
@param aSetMask Mask of bits to set (1 = set this bit) |
|
346 |
@return The original value of FPEXC, 0 if no VFP present |
|
347 |
||
348 |
@publishedPartner |
|
349 |
@released |
|
350 |
*/ |
|
351 |
EXPORT_C __NAKED__ TUint32 Arm::ModifyFpExc(TUint32 /*aClearMask*/, TUint32 /*aSetMask*/) |
|
352 |
{ |
|
353 |
#ifdef __CPU_HAS_VFP |
|
354 |
VFP_FMRX(,12,VFP_XREG_FPEXC); |
|
355 |
asm("bic r0, r12, r0 "); |
|
356 |
asm("orr r0, r0, r1 "); |
|
357 |
||
358 |
#if defined(__CPU_ARM1136__) && !defined(__CPU_ARM1136_ERRATUM_351912_FIXED) |
|
359 |
// If we are about to enable VFP, disable dynamic branch prediction |
|
360 |
// If we are about to disable VFP, enable dynamic branch prediction if return stack prediction is enabled |
|
361 |
asm("mrs r3, cpsr "); |
|
362 |
CPSIDAIF; |
|
363 |
asm("mrc p15, 0, r1, c1, c0, 1 "); |
|
364 |
asm("tst r0, #%a0" : : "i" ((TInt)VFP_FPEXC_EN) ); |
|
365 |
asm("bic r1, r1, #2 "); // clear DB bit (disable dynamic prediction) |
|
366 |
asm("and r2, r1, #1 "); // r2 bit 0 = RS bit (1 if return stack enabled) |
|
367 |
asm("orreq r1, r1, r2, lsl #1 "); // if VFP is being disabled set DB = RS |
|
368 |
asm("mcr p15, 0, r1, c1, c0, 1 "); |
|
369 |
asm("mcr p15, 0, r2, c7, c5, 6 "); // flush BTAC |
|
370 |
VFP_FMXR(,VFP_XREG_FPEXC,0); |
|
371 |
asm("msr cpsr, r3 "); |
|
372 |
#else |
|
373 |
VFP_FMXR(,VFP_XREG_FPEXC,0); |
|
374 |
#endif // erratum 351912 |
|
375 |
||
376 |
asm("mov r0, r12 "); |
|
377 |
#else // no vfp |
|
378 |
asm("mov r0, #0 "); |
|
379 |
#endif |
|
380 |
__JUMP(,lr); |
|
381 |
} |
|
382 |
||
383 |
/** Get the value of the VFP FPSCR register |
|
384 |
||
385 |
@return The value of FPSCR, 0 if there is no VFP |
|
386 |
||
387 |
@publishedPartner |
|
388 |
@released |
|
389 |
*/ |
|
390 |
EXPORT_C __NAKED__ TUint32 Arm::FpScr() |
|
391 |
{ |
|
392 |
#ifdef __CPU_HAS_VFP |
|
393 |
VFP_FMRX(,0,VFP_XREG_FPSCR); |
|
394 |
#else |
|
395 |
asm("mov r0, #0 "); |
|
396 |
#endif |
|
397 |
__JUMP(,lr); |
|
398 |
} |
|
399 |
||
400 |
||
401 |
||
402 |
/** Modify the VFP FPSCR register |
|
403 |
Does nothing if there is no VFP |
|
404 |
||
405 |
@param aClearMask Mask of bits to clear (1 = clear this bit) |
|
406 |
@param aSetMask Mask of bits to set (1 = set this bit) |
|
407 |
@return The original value of FPSCR, 0 if no VFP present |
|
408 |
||
409 |
@publishedPartner |
|
410 |
@released |
|
411 |
*/ |
|
412 |
EXPORT_C __NAKED__ TUint32 Arm::ModifyFpScr(TUint32 /*aClearMask*/, TUint32 /*aSetMask*/) |
|
413 |
{ |
|
414 |
#ifdef __CPU_HAS_VFP |
|
415 |
VFP_FMRX(,2,VFP_XREG_FPSCR); |
|
416 |
asm("bic r0, r2, r0 "); |
|
417 |
asm("orr r0, r0, r1 "); |
|
418 |
VFP_FMXR(,VFP_XREG_FPSCR,0); |
|
419 |
asm("mov r0, r2 "); |
|
420 |
#else |
|
421 |
asm("mov r0, #0 "); |
|
422 |
#endif |
|
423 |
__JUMP(,lr); |
|
424 |
} |
|
425 |
||
426 |
||
427 |
/** Detect whether NEON is present |
|
428 |
||
429 |
@return ETrue if present, EFalse if not |
|
430 |
||
431 |
@internalTechnology |
|
432 |
@released |
|
433 |
*/ |
|
434 |
#if defined(__CPU_HAS_VFP) && defined(__VFP_V3) |
|
435 |
__NAKED__ TBool Arm::NeonPresent() |
|
436 |
{ |
|
437 |
asm("mov r0, #0 "); // Not present |
|
438 |
VFP_FMRX(, 1,VFP_XREG_FPEXC); // Save VFP state |
|
439 |
asm("orr r2, r1, #%a0" : : "i" ((TInt)VFP_FPEXC_EN)); |
|
440 |
VFP_FMXR(, VFP_XREG_FPEXC,1); // Enable VFP |
|
441 |
||
442 |
VFP_FMRX(, 2,VFP_XREG_MVFR0); // Read MVFR0 |
|
443 |
asm("tst r2, #%a0" : : "i" ((TInt)VFP_MVFR0_ASIMD32)); // Check to see if all 32 Advanced SIMD registers are present |
|
444 |
asm("beq 0f "); // Skip ahead if not |
|
445 |
GET_CAR(, r2); |
|
446 |
asm("tst r2, #%a0" : : "i" ((TInt)VFP_CPACR_ASEDIS)); // Check to see if ASIMD is disabled |
|
447 |
asm("bne 0f "); // Skip ahead if so |
|
448 |
asm("tst r2, #%a0" : : "i" ((TInt)VFP_CPACR_D32DIS)); // Check to see if the upper 16 registers are disabled |
|
449 |
asm("moveq r0, #1" ); // If not then eport NEON present |
|
450 |
||
451 |
asm("0: "); |
|
452 |
VFP_FMXR(,VFP_XREG_FPEXC,1); // Restore VFP state |
|
453 |
__JUMP(, lr); |
|
454 |
} |
|
455 |
#endif |
|
456 |
||
457 |
||
458 |
#ifdef __CPU_HAS_MMU |
|
459 |
__NAKED__ TBool Arm::MmuActive() |
|
460 |
{ |
|
461 |
asm("mrc p15, 0, r0, c1, c0, 0 "); |
|
462 |
asm("and r0, r0, #1 "); |
|
463 |
__JUMP(,lr); |
|
464 |
} |
|
465 |
||
466 |
// Returns the content of Translate Table Base Register 0. |
|
467 |
// To get physical address of the level 1 table, on some platforms this must be orred with 0xffff8000 (to get rid of table walk cache attributes) |
|
468 |
__NAKED__ TUint32 Arm::MmuTTBR0() |
|
469 |
{ |
|
470 |
asm("mrc p15, 0, r0, c2, c0, 0 "); |
|
471 |
__JUMP(,lr); |
|
472 |
} |
|
473 |
#endif |
|
474 |
||
475 |
||
476 |
||
477 |
/** Get the current value of the high performance counter. |
|
478 |
||
479 |
If a high performance counter is not available, this uses the millisecond |
|
480 |
tick count instead. |
|
481 |
*/ |
|
482 |
#ifdef HAS_HIGH_RES_TIMER |
|
483 |
EXPORT_C __NAKED__ TUint32 NKern::FastCounter() |
|
484 |
{ |
|
485 |
GET_HIGH_RES_TICK_COUNT(R0); |
|
486 |
__JUMP(,lr); |
|
487 |
} |
|
488 |
#else |
|
489 |
EXPORT_C TUint32 NKern::FastCounter() |
|
490 |
{ |
|
491 |
return NTickCount(); |
|
492 |
} |
|
493 |
#endif |
|
494 |
||
495 |
||
496 |
||
497 |
/** Get the frequency of counter queried by NKern::FastCounter(). |
|
498 |
*/ |
|
499 |
EXPORT_C TInt NKern::FastCounterFrequency() |
|
500 |
{ |
|
501 |
#ifdef HAS_HIGH_RES_TIMER |
|
502 |
return KHighResTimerFrequency; |
|
503 |
#else |
|
504 |
return 1000000 / NKern::TickPeriod(); |
|
505 |
#endif |
|
506 |
} |
|
507 |