Merge tag 'arm64-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cmarinas...
[~shefty/rdma-dev.git] / arch / arm64 / kernel / vdso / gettimeofday.S
1 /*
2  * Userspace implementations of gettimeofday() and friends.
3  *
4  * Copyright (C) 2012 ARM Limited
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * Author: Will Deacon <will.deacon@arm.com>
19  */
20
21 #include <linux/linkage.h>
22 #include <asm/asm-offsets.h>
23 #include <asm/unistd.h>
24
25 #define NSEC_PER_SEC_LO16       0xca00
26 #define NSEC_PER_SEC_HI16       0x3b9a
27
28 vdso_data       .req    x6
29 use_syscall     .req    w7
30 seqcnt          .req    w8
31
32         .macro  seqcnt_acquire
33 9999:   ldr     seqcnt, [vdso_data, #VDSO_TB_SEQ_COUNT]
34         tbnz    seqcnt, #0, 9999b
35         dmb     ishld
36         ldr     use_syscall, [vdso_data, #VDSO_USE_SYSCALL]
37         .endm
38
39         .macro  seqcnt_read, cnt
40         dmb     ishld
41         ldr     \cnt, [vdso_data, #VDSO_TB_SEQ_COUNT]
42         .endm
43
44         .macro  seqcnt_check, cnt, fail
45         cmp     \cnt, seqcnt
46         b.ne    \fail
47         .endm
48
49         .text
50
51 /* int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz); */
52 ENTRY(__kernel_gettimeofday)
53         .cfi_startproc
54         mov     x2, x30
55         .cfi_register x30, x2
56
57         /* Acquire the sequence counter and get the timespec. */
58         adr     vdso_data, _vdso_data
59 1:      seqcnt_acquire
60         cbnz    use_syscall, 4f
61
62         /* If tv is NULL, skip to the timezone code. */
63         cbz     x0, 2f
64         bl      __do_get_tspec
65         seqcnt_check w9, 1b
66
67         /* Convert ns to us. */
68         mov     x13, #1000
69         lsl     x13, x13, x12
70         udiv    x11, x11, x13
71         stp     x10, x11, [x0, #TVAL_TV_SEC]
72 2:
73         /* If tz is NULL, return 0. */
74         cbz     x1, 3f
75         ldp     w4, w5, [vdso_data, #VDSO_TZ_MINWEST]
76         seqcnt_read w9
77         seqcnt_check w9, 1b
78         stp     w4, w5, [x1, #TZ_MINWEST]
79 3:
80         mov     x0, xzr
81         ret     x2
82 4:
83         /* Syscall fallback. */
84         mov     x8, #__NR_gettimeofday
85         svc     #0
86         ret     x2
87         .cfi_endproc
88 ENDPROC(__kernel_gettimeofday)
89
90 /* int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); */
91 ENTRY(__kernel_clock_gettime)
92         .cfi_startproc
93         cmp     w0, #CLOCK_REALTIME
94         ccmp    w0, #CLOCK_MONOTONIC, #0x4, ne
95         b.ne    2f
96
97         mov     x2, x30
98         .cfi_register x30, x2
99
100         /* Get kernel timespec. */
101         adr     vdso_data, _vdso_data
102 1:      seqcnt_acquire
103         cbnz    use_syscall, 7f
104
105         bl      __do_get_tspec
106         seqcnt_check w9, 1b
107
108         cmp     w0, #CLOCK_MONOTONIC
109         b.ne    6f
110
111         /* Get wtm timespec. */
112         ldp     x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC]
113
114         /* Check the sequence counter. */
115         seqcnt_read w9
116         seqcnt_check w9, 1b
117         b       4f
118 2:
119         cmp     w0, #CLOCK_REALTIME_COARSE
120         ccmp    w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
121         b.ne    8f
122
123         /* Get coarse timespec. */
124         adr     vdso_data, _vdso_data
125 3:      seqcnt_acquire
126         ldp     x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC]
127
128         /* Get wtm timespec. */
129         ldp     x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC]
130
131         /* Check the sequence counter. */
132         seqcnt_read w9
133         seqcnt_check w9, 3b
134
135         cmp     w0, #CLOCK_MONOTONIC_COARSE
136         b.ne    6f
137 4:
138         /* Add on wtm timespec. */
139         add     x10, x10, x13
140         lsl     x14, x14, x12
141         add     x11, x11, x14
142
143         /* Normalise the new timespec. */
144         mov     x15, #NSEC_PER_SEC_LO16
145         movk    x15, #NSEC_PER_SEC_HI16, lsl #16
146         lsl     x15, x15, x12
147         cmp     x11, x15
148         b.lt    5f
149         sub     x11, x11, x15
150         add     x10, x10, #1
151 5:
152         cmp     x11, #0
153         b.ge    6f
154         add     x11, x11, x15
155         sub     x10, x10, #1
156
157 6:      /* Store to the user timespec. */
158         lsr     x11, x11, x12
159         stp     x10, x11, [x1, #TSPEC_TV_SEC]
160         mov     x0, xzr
161         ret     x2
162 7:
163         mov     x30, x2
164 8:      /* Syscall fallback. */
165         mov     x8, #__NR_clock_gettime
166         svc     #0
167         ret
168         .cfi_endproc
169 ENDPROC(__kernel_clock_gettime)
170
171 /* int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); */
172 ENTRY(__kernel_clock_getres)
173         .cfi_startproc
174         cbz     w1, 3f
175
176         cmp     w0, #CLOCK_REALTIME
177         ccmp    w0, #CLOCK_MONOTONIC, #0x4, ne
178         b.ne    1f
179
180         ldr     x2, 5f
181         b       2f
182 1:
183         cmp     w0, #CLOCK_REALTIME_COARSE
184         ccmp    w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
185         b.ne    4f
186         ldr     x2, 6f
187 2:
188         stp     xzr, x2, [x1]
189
190 3:      /* res == NULL. */
191         mov     w0, wzr
192         ret
193
194 4:      /* Syscall fallback. */
195         mov     x8, #__NR_clock_getres
196         svc     #0
197         ret
198 5:
199         .quad   CLOCK_REALTIME_RES
200 6:
201         .quad   CLOCK_COARSE_RES
202         .cfi_endproc
203 ENDPROC(__kernel_clock_getres)
204
205 /*
206  * Read the current time from the architected counter.
207  * Expects vdso_data to be initialised.
208  * Clobbers the temporary registers (x9 - x15).
209  * Returns:
210  *  - w9                = vDSO sequence counter
211  *  - (x10, x11)        = (ts->tv_sec, shifted ts->tv_nsec)
212  *  - w12               = cs_shift
213  */
214 ENTRY(__do_get_tspec)
215         .cfi_startproc
216
217         /* Read from the vDSO data page. */
218         ldr     x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
219         ldp     x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
220         ldp     w11, w12, [vdso_data, #VDSO_CS_MULT]
221         seqcnt_read w9
222
223         /* Read the virtual counter. */
224         isb
225         mrs     x15, cntvct_el0
226
227         /* Calculate cycle delta and convert to ns. */
228         sub     x10, x15, x10
229         /* We can only guarantee 56 bits of precision. */
230         movn    x15, #0xff00, lsl #48
231         and     x10, x15, x10
232         mul     x10, x10, x11
233
234         /* Use the kernel time to calculate the new timespec. */
235         mov     x11, #NSEC_PER_SEC_LO16
236         movk    x11, #NSEC_PER_SEC_HI16, lsl #16
237         lsl     x11, x11, x12
238         add     x15, x10, x14
239         udiv    x14, x15, x11
240         add     x10, x13, x14
241         mul     x13, x14, x11
242         sub     x11, x15, x13
243
244         ret
245         .cfi_endproc
246 ENDPROC(__do_get_tspec)