Merge branch 'omap-serial' of git://git.linaro.org/people/rmk/linux-arm
[~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 w13, 1b
66
67         /* Convert ns to us. */
68         mov     x11, #1000
69         udiv    x10, x10, x11
70         stp     x9, x10, [x0, #TVAL_TV_SEC]
71 2:
72         /* If tz is NULL, return 0. */
73         cbz     x1, 3f
74         ldp     w4, w5, [vdso_data, #VDSO_TZ_MINWEST]
75         seqcnt_read w13
76         seqcnt_check w13, 1b
77         stp     w4, w5, [x1, #TZ_MINWEST]
78 3:
79         mov     x0, xzr
80         ret     x2
81 4:
82         /* Syscall fallback. */
83         mov     x8, #__NR_gettimeofday
84         svc     #0
85         ret     x2
86         .cfi_endproc
87 ENDPROC(__kernel_gettimeofday)
88
89 /* int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); */
90 ENTRY(__kernel_clock_gettime)
91         .cfi_startproc
92         cmp     w0, #CLOCK_REALTIME
93         ccmp    w0, #CLOCK_MONOTONIC, #0x4, ne
94         b.ne    2f
95
96         mov     x2, x30
97         .cfi_register x30, x2
98
99         /* Get kernel timespec. */
100         adr     vdso_data, _vdso_data
101 1:      seqcnt_acquire
102         cbnz    use_syscall, 7f
103
104         bl      __do_get_tspec
105         seqcnt_check w13, 1b
106
107         cmp     w0, #CLOCK_MONOTONIC
108         b.ne    6f
109
110         /* Get wtm timespec. */
111         ldp     x14, x15, [vdso_data, #VDSO_WTM_CLK_SEC]
112
113         /* Check the sequence counter. */
114         seqcnt_read w13
115         seqcnt_check w13, 1b
116         b       4f
117 2:
118         cmp     w0, #CLOCK_REALTIME_COARSE
119         ccmp    w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
120         b.ne    8f
121
122         /* Get coarse timespec. */
123         adr     vdso_data, _vdso_data
124 3:      seqcnt_acquire
125         ldp     x9, x10, [vdso_data, #VDSO_XTIME_CRS_SEC]
126
127         cmp     w0, #CLOCK_MONOTONIC_COARSE
128         b.ne    6f
129
130         /* Get wtm timespec. */
131         ldp     x14, x15, [vdso_data, #VDSO_WTM_CLK_SEC]
132
133         /* Check the sequence counter. */
134         seqcnt_read w13
135         seqcnt_check w13, 3b
136 4:
137         /* Add on wtm timespec. */
138         add     x9, x9, x14
139         add     x10, x10, x15
140
141         /* Normalise the new timespec. */
142         mov     x14, #NSEC_PER_SEC_LO16
143         movk    x14, #NSEC_PER_SEC_HI16, lsl #16
144         cmp     x10, x14
145         b.lt    5f
146         sub     x10, x10, x14
147         add     x9, x9, #1
148 5:
149         cmp     x10, #0
150         b.ge    6f
151         add     x10, x10, x14
152         sub     x9, x9, #1
153
154 6:      /* Store to the user timespec. */
155         stp     x9, x10, [x1, #TSPEC_TV_SEC]
156         mov     x0, xzr
157         ret     x2
158 7:
159         mov     x30, x2
160 8:      /* Syscall fallback. */
161         mov     x8, #__NR_clock_gettime
162         svc     #0
163         ret
164         .cfi_endproc
165 ENDPROC(__kernel_clock_gettime)
166
167 /* int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); */
168 ENTRY(__kernel_clock_getres)
169         .cfi_startproc
170         cbz     w1, 3f
171
172         cmp     w0, #CLOCK_REALTIME
173         ccmp    w0, #CLOCK_MONOTONIC, #0x4, ne
174         b.ne    1f
175
176         ldr     x2, 5f
177         b       2f
178 1:
179         cmp     w0, #CLOCK_REALTIME_COARSE
180         ccmp    w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
181         b.ne    4f
182         ldr     x2, 6f
183 2:
184         stp     xzr, x2, [x1]
185
186 3:      /* res == NULL. */
187         mov     w0, wzr
188         ret
189
190 4:      /* Syscall fallback. */
191         mov     x8, #__NR_clock_getres
192         svc     #0
193         ret
194 5:
195         .quad   CLOCK_REALTIME_RES
196 6:
197         .quad   CLOCK_COARSE_RES
198         .cfi_endproc
199 ENDPROC(__kernel_clock_getres)
200
201 /*
202  * Read the current time from the architected counter.
203  * Expects vdso_data to be initialised.
204  * Clobbers the temporary registers (x9 - x15).
205  * Returns:
206  *  - (x9, x10) = (ts->tv_sec, ts->tv_nsec)
207  *  - (x11, x12) = (xtime->tv_sec, xtime->tv_nsec)
208  *  - w13 = vDSO sequence counter
209  */
210 ENTRY(__do_get_tspec)
211         .cfi_startproc
212
213         /* Read from the vDSO data page. */
214         ldr     x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
215         ldp     x11, x12, [vdso_data, #VDSO_XTIME_CLK_SEC]
216         ldp     w14, w15, [vdso_data, #VDSO_CS_MULT]
217         seqcnt_read w13
218
219         /* Read the physical counter. */
220         isb
221         mrs     x9, cntpct_el0
222
223         /* Calculate cycle delta and convert to ns. */
224         sub     x10, x9, x10
225         /* We can only guarantee 56 bits of precision. */
226         movn    x9, #0xff0, lsl #48
227         and     x10, x9, x10
228         mul     x10, x10, x14
229         lsr     x10, x10, x15
230
231         /* Use the kernel time to calculate the new timespec. */
232         add     x10, x12, x10
233         mov     x14, #NSEC_PER_SEC_LO16
234         movk    x14, #NSEC_PER_SEC_HI16, lsl #16
235         udiv    x15, x10, x14
236         add     x9, x15, x11
237         mul     x14, x14, x15
238         sub     x10, x10, x14
239
240         ret
241         .cfi_endproc
242 ENDPROC(__do_get_tspec)