Commit | Line | Data |
---|---|---|
a7f290da BH |
1 | /* |
2 | * Userland implementation of gettimeofday() for 32 bits processes in a | |
3 | * ppc64 kernel for use in the vDSO | |
4 | * | |
5 | * Copyright (C) 2004 Benjamin Herrenschmuidt (benh@kernel.crashing.org, | |
6 | * IBM Corp. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License | |
10 | * as published by the Free Software Foundation; either version | |
11 | * 2 of the License, or (at your option) any later version. | |
12 | */ | |
a7f290da BH |
13 | #include <asm/processor.h> |
14 | #include <asm/ppc_asm.h> | |
15 | #include <asm/vdso.h> | |
16 | #include <asm/asm-offsets.h> | |
17 | #include <asm/unistd.h> | |
18 | ||
597bc5c0 PM |
19 | /* Offset for the low 32-bit part of a field of long type */ |
20 | #ifdef CONFIG_PPC64 | |
21 | #define LOPART 4 | |
8fd63a9e | 22 | #define TSPEC_TV_SEC TSPC64_TV_SEC+LOPART |
597bc5c0 PM |
23 | #else |
24 | #define LOPART 0 | |
8fd63a9e | 25 | #define TSPEC_TV_SEC TSPC32_TV_SEC |
597bc5c0 PM |
26 | #endif |
27 | ||
a7f290da BH |
28 | .text |
29 | /* | |
30 | * Exact prototype of gettimeofday | |
31 | * | |
32 | * int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz); | |
33 | * | |
34 | */ | |
35 | V_FUNCTION_BEGIN(__kernel_gettimeofday) | |
36 | .cfi_startproc | |
37 | mflr r12 | |
38 | .cfi_register lr,r12 | |
39 | ||
40 | mr r10,r3 /* r10 saves tv */ | |
41 | mr r11,r4 /* r11 saves tz */ | |
42 | bl __get_datapage@local /* get data page */ | |
43 | mr r9, r3 /* datapage ptr in r9 */ | |
74609f45 TB |
44 | cmplwi r10,0 /* check if tv is NULL */ |
45 | beq 3f | |
8fd63a9e PM |
46 | lis r7,1000000@ha /* load up USEC_PER_SEC */ |
47 | addi r7,r7,1000000@l /* so we get microseconds in r4 */ | |
48 | bl __do_get_tspec@local /* get sec/usec from tb & kernel */ | |
49 | stw r3,TVAL32_TV_SEC(r10) | |
50 | stw r4,TVAL32_TV_USEC(r10) | |
a7f290da | 51 | |
74609f45 | 52 | 3: cmplwi r11,0 /* check if tz is NULL */ |
a7f290da BH |
53 | beq 1f |
54 | lwz r4,CFG_TZ_MINUTEWEST(r9)/* fill tz */ | |
55 | lwz r5,CFG_TZ_DSTTIME(r9) | |
56 | stw r4,TZONE_TZ_MINWEST(r11) | |
57 | stw r5,TZONE_TZ_DSTTIME(r11) | |
58 | ||
59 | 1: mtlr r12 | |
5d66da3d | 60 | crclr cr0*4+so |
a7f290da BH |
61 | li r3,0 |
62 | blr | |
a7f290da BH |
63 | .cfi_endproc |
64 | V_FUNCTION_END(__kernel_gettimeofday) | |
65 | ||
66 | /* | |
67 | * Exact prototype of clock_gettime() | |
68 | * | |
69 | * int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); | |
70 | * | |
71 | */ | |
72 | V_FUNCTION_BEGIN(__kernel_clock_gettime) | |
73 | .cfi_startproc | |
74 | /* Check for supported clock IDs */ | |
75 | cmpli cr0,r3,CLOCK_REALTIME | |
76 | cmpli cr1,r3,CLOCK_MONOTONIC | |
0c37ec2a | 77 | cror cr0*4+eq,cr0*4+eq,cr1*4+eq |
a7f290da BH |
78 | bne cr0,99f |
79 | ||
80 | mflr r12 /* r12 saves lr */ | |
81 | .cfi_register lr,r12 | |
a7f290da BH |
82 | mr r11,r4 /* r11 saves tp */ |
83 | bl __get_datapage@local /* get data page */ | |
0c37ec2a | 84 | mr r9,r3 /* datapage ptr in r9 */ |
8fd63a9e PM |
85 | lis r7,NSEC_PER_SEC@h /* want nanoseconds */ |
86 | ori r7,r7,NSEC_PER_SEC@l | |
597bc5c0 PM |
87 | 50: bl __do_get_tspec@local /* get sec/nsec from tb & kernel */ |
88 | bne cr1,80f /* not monotonic -> all done */ | |
a7f290da BH |
89 | |
90 | /* | |
91 | * CLOCK_MONOTONIC | |
92 | */ | |
93 | ||
a7f290da BH |
94 | /* now we must fixup using wall to monotonic. We need to snapshot |
95 | * that value and do the counter trick again. Fortunately, we still | |
96 | * have the counter value in r8 that was returned by __do_get_xsec. | |
597bc5c0 PM |
97 | * At this point, r3,r4 contain our sec/nsec values, r5 and r6 |
98 | * can be used, r7 contains NSEC_PER_SEC. | |
a7f290da BH |
99 | */ |
100 | ||
597bc5c0 PM |
101 | lwz r5,WTOM_CLOCK_SEC(r9) |
102 | lwz r6,WTOM_CLOCK_NSEC(r9) | |
a7f290da | 103 | |
597bc5c0 PM |
104 | /* We now have our offset in r5,r6. We create a fake dependency |
105 | * on that value and re-check the counter | |
a7f290da | 106 | */ |
597bc5c0 PM |
107 | or r0,r6,r5 |
108 | xor r0,r0,r0 | |
a7f290da | 109 | add r9,r9,r0 |
597bc5c0 | 110 | lwz r0,(CFG_TB_UPDATE_COUNT+LOPART)(r9) |
a7f290da BH |
111 | cmpl cr0,r8,r0 /* check if updated */ |
112 | bne- 50b | |
113 | ||
597bc5c0 | 114 | /* Calculate and store result. Note that this mimics the C code, |
a7f290da BH |
115 | * which may cause funny results if nsec goes negative... is that |
116 | * possible at all ? | |
117 | */ | |
597bc5c0 PM |
118 | add r3,r3,r5 |
119 | add r4,r4,r6 | |
120 | cmpw cr0,r4,r7 | |
121 | cmpwi cr1,r4,0 | |
a7f290da | 122 | blt 1f |
597bc5c0 | 123 | subf r4,r7,r4 |
a7f290da | 124 | addi r3,r3,1 |
597bc5c0 | 125 | 1: bge cr1,80f |
0c37ec2a | 126 | addi r3,r3,-1 |
597bc5c0 PM |
127 | add r4,r4,r7 |
128 | ||
129 | 80: stw r3,TSPC32_TV_SEC(r11) | |
a7f290da BH |
130 | stw r4,TSPC32_TV_NSEC(r11) |
131 | ||
132 | mtlr r12 | |
5d66da3d | 133 | crclr cr0*4+so |
a7f290da BH |
134 | li r3,0 |
135 | blr | |
136 | ||
137 | /* | |
138 | * syscall fallback | |
139 | */ | |
a7f290da BH |
140 | 99: |
141 | li r0,__NR_clock_gettime | |
142 | sc | |
143 | blr | |
144 | .cfi_endproc | |
145 | V_FUNCTION_END(__kernel_clock_gettime) | |
146 | ||
147 | ||
148 | /* | |
149 | * Exact prototype of clock_getres() | |
150 | * | |
151 | * int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); | |
152 | * | |
153 | */ | |
154 | V_FUNCTION_BEGIN(__kernel_clock_getres) | |
155 | .cfi_startproc | |
156 | /* Check for supported clock IDs */ | |
157 | cmpwi cr0,r3,CLOCK_REALTIME | |
158 | cmpwi cr1,r3,CLOCK_MONOTONIC | |
0c37ec2a | 159 | cror cr0*4+eq,cr0*4+eq,cr1*4+eq |
a7f290da BH |
160 | bne cr0,99f |
161 | ||
162 | li r3,0 | |
163 | cmpli cr0,r4,0 | |
5d66da3d | 164 | crclr cr0*4+so |
a7f290da BH |
165 | beqlr |
166 | lis r5,CLOCK_REALTIME_RES@h | |
167 | ori r5,r5,CLOCK_REALTIME_RES@l | |
168 | stw r3,TSPC32_TV_SEC(r4) | |
169 | stw r5,TSPC32_TV_NSEC(r4) | |
170 | blr | |
171 | ||
172 | /* | |
173 | * syscall fallback | |
174 | */ | |
175 | 99: | |
176 | li r0,__NR_clock_getres | |
177 | sc | |
178 | blr | |
179 | .cfi_endproc | |
180 | V_FUNCTION_END(__kernel_clock_getres) | |
181 | ||
182 | ||
fcb41a20 AZ |
183 | /* |
184 | * Exact prototype of time() | |
185 | * | |
186 | * time_t time(time *t); | |
187 | * | |
188 | */ | |
189 | V_FUNCTION_BEGIN(__kernel_time) | |
190 | .cfi_startproc | |
191 | mflr r12 | |
192 | .cfi_register lr,r12 | |
193 | ||
194 | mr r11,r3 /* r11 holds t */ | |
195 | bl __get_datapage@local | |
196 | mr r9, r3 /* datapage ptr in r9 */ | |
197 | ||
198 | lwz r3,STAMP_XTIME+TSPEC_TV_SEC(r9) | |
199 | ||
200 | cmplwi r11,0 /* check if t is NULL */ | |
201 | beq 2f | |
202 | stw r3,0(r11) /* store result at *t */ | |
203 | 2: mtlr r12 | |
204 | crclr cr0*4+so | |
205 | blr | |
206 | .cfi_endproc | |
207 | V_FUNCTION_END(__kernel_time) | |
208 | ||
a7f290da | 209 | /* |
8fd63a9e PM |
210 | * This is the core of clock_gettime() and gettimeofday(), |
211 | * it returns the current time in r3 (seconds) and r4. | |
212 | * On entry, r7 gives the resolution of r4, either USEC_PER_SEC | |
213 | * or NSEC_PER_SEC, giving r4 in microseconds or nanoseconds. | |
597bc5c0 | 214 | * It expects the datapage ptr in r9 and doesn't clobber it. |
8fd63a9e | 215 | * It clobbers r0, r5 and r6. |
597bc5c0 PM |
216 | * On return, r8 contains the counter value that can be reused. |
217 | * This clobbers cr0 but not any other cr field. | |
218 | */ | |
219 | __do_get_tspec: | |
220 | .cfi_startproc | |
221 | /* Check for update count & load values. We use the low | |
222 | * order 32 bits of the update count | |
223 | */ | |
224 | 1: lwz r8,(CFG_TB_UPDATE_COUNT+LOPART)(r9) | |
225 | andi. r0,r8,1 /* pending update ? loop */ | |
226 | bne- 1b | |
227 | xor r0,r8,r8 /* create dependency */ | |
228 | add r9,r9,r0 | |
229 | ||
230 | /* Load orig stamp (offset to TB) */ | |
231 | lwz r5,CFG_TB_ORIG_STAMP(r9) | |
232 | lwz r6,(CFG_TB_ORIG_STAMP+4)(r9) | |
233 | ||
234 | /* Get a stable TB value */ | |
ae2163be LC |
235 | #ifdef CONFIG_8xx |
236 | 2: mftbu r3 | |
237 | mftbl r4 | |
238 | mftbu r0 | |
239 | #else | |
beb2dc0a SW |
240 | 2: mfspr r3, SPRN_TBRU |
241 | mfspr r4, SPRN_TBRL | |
242 | mfspr r0, SPRN_TBRU | |
ae2163be | 243 | #endif |
8fd63a9e | 244 | cmplw cr0,r3,r0 |
597bc5c0 PM |
245 | bne- 2b |
246 | ||
247 | /* Subtract tb orig stamp and shift left 12 bits. | |
248 | */ | |
8fd63a9e | 249 | subfc r4,r6,r4 |
597bc5c0 PM |
250 | subfe r0,r5,r3 |
251 | slwi r0,r0,12 | |
8fd63a9e PM |
252 | rlwimi. r0,r4,12,20,31 |
253 | slwi r4,r4,12 | |
597bc5c0 | 254 | |
8fd63a9e PM |
255 | /* |
256 | * Load scale factor & do multiplication. | |
257 | * We only use the high 32 bits of the tb_to_xs value. | |
258 | * Even with a 1GHz timebase clock, the high 32 bits of | |
259 | * tb_to_xs will be at least 4 million, so the error from | |
260 | * ignoring the low 32 bits will be no more than 0.25ppm. | |
261 | * The error will just make the clock run very very slightly | |
262 | * slow until the next time the kernel updates the VDSO data, | |
263 | * at which point the clock will catch up to the kernel's value, | |
264 | * so there is no long-term error accumulation. | |
265 | */ | |
597bc5c0 | 266 | lwz r5,CFG_TB_TO_XS(r9) /* load values */ |
8fd63a9e | 267 | mulhwu r4,r4,r5 |
597bc5c0 PM |
268 | li r3,0 |
269 | ||
270 | beq+ 4f /* skip high part computation if 0 */ | |
271 | mulhwu r3,r0,r5 | |
8fd63a9e | 272 | mullw r5,r0,r5 |
597bc5c0 PM |
273 | addc r4,r4,r5 |
274 | addze r3,r3 | |
8fd63a9e PM |
275 | 4: |
276 | /* At this point, we have seconds since the xtime stamp | |
277 | * as a 32.32 fixed-point number in r3 and r4. | |
278 | * Load & add the xtime stamp. | |
597bc5c0 | 279 | */ |
8fd63a9e PM |
280 | lwz r5,STAMP_XTIME+TSPEC_TV_SEC(r9) |
281 | lwz r6,STAMP_SEC_FRAC(r9) | |
282 | addc r4,r4,r6 | |
597bc5c0 PM |
283 | adde r3,r3,r5 |
284 | ||
8fd63a9e PM |
285 | /* We create a fake dependency on the result in r3/r4 |
286 | * and re-check the counter | |
597bc5c0 PM |
287 | */ |
288 | or r6,r4,r3 | |
289 | xor r0,r6,r6 | |
290 | add r9,r9,r0 | |
291 | lwz r0,(CFG_TB_UPDATE_COUNT+LOPART)(r9) | |
8fd63a9e | 292 | cmplw cr0,r8,r0 /* check if updated */ |
597bc5c0 PM |
293 | bne- 1b |
294 | ||
8fd63a9e | 295 | mulhwu r4,r4,r7 /* convert to micro or nanoseconds */ |
597bc5c0 PM |
296 | |
297 | blr | |
298 | .cfi_endproc |