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
28vdso_data	.req	x6
29use_syscall	.req	w7
30seqcnt		.req	w8
31
32	.macro	seqcnt_acquire
339999:	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); */
52ENTRY(__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
591:	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]
722:
73	/* If tz is NULL, return 0. */
74	cbz	x1, 3f
75	ldp	w4, w5, [vdso_data, #VDSO_TZ_MINWEST]
76	stp	w4, w5, [x1, #TZ_MINWEST]
773:
78	mov	x0, xzr
79	ret	x2
804:
81	/* Syscall fallback. */
82	mov	x8, #__NR_gettimeofday
83	svc	#0
84	ret	x2
85	.cfi_endproc
86ENDPROC(__kernel_gettimeofday)
87
88/* int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); */
89ENTRY(__kernel_clock_gettime)
90	.cfi_startproc
91	cmp	w0, #CLOCK_REALTIME
92	ccmp	w0, #CLOCK_MONOTONIC, #0x4, ne
93	b.ne	2f
94
95	mov	x2, x30
96	.cfi_register x30, x2
97
98	/* Get kernel timespec. */
99	adr	vdso_data, _vdso_data
1001:	seqcnt_acquire
101	cbnz	use_syscall, 7f
102
103	bl	__do_get_tspec
104	seqcnt_check w9, 1b
105
106	mov	x30, x2
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
1182:
119	cmp	w0, #CLOCK_REALTIME_COARSE
120	ccmp	w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
121	b.ne	8f
122
123	/* xtime_coarse_nsec is already right-shifted */
124	mov	x12, #0
125
126	/* Get coarse timespec. */
127	adr	vdso_data, _vdso_data
1283:	seqcnt_acquire
129	ldp	x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC]
130
131	/* Get wtm timespec. */
132	ldp	x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC]
133
134	/* Check the sequence counter. */
135	seqcnt_read w9
136	seqcnt_check w9, 3b
137
138	cmp	w0, #CLOCK_MONOTONIC_COARSE
139	b.ne	6f
1404:
141	/* Add on wtm timespec. */
142	add	x10, x10, x13
143	lsl	x14, x14, x12
144	add	x11, x11, x14
145
146	/* Normalise the new timespec. */
147	mov	x15, #NSEC_PER_SEC_LO16
148	movk	x15, #NSEC_PER_SEC_HI16, lsl #16
149	lsl	x15, x15, x12
150	cmp	x11, x15
151	b.lt	5f
152	sub	x11, x11, x15
153	add	x10, x10, #1
1545:
155	cmp	x11, #0
156	b.ge	6f
157	add	x11, x11, x15
158	sub	x10, x10, #1
159
1606:	/* Store to the user timespec. */
161	lsr	x11, x11, x12
162	stp	x10, x11, [x1, #TSPEC_TV_SEC]
163	mov	x0, xzr
164	ret
1657:
166	mov	x30, x2
1678:	/* Syscall fallback. */
168	mov	x8, #__NR_clock_gettime
169	svc	#0
170	ret
171	.cfi_endproc
172ENDPROC(__kernel_clock_gettime)
173
174/* int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); */
175ENTRY(__kernel_clock_getres)
176	.cfi_startproc
177	cmp	w0, #CLOCK_REALTIME
178	ccmp	w0, #CLOCK_MONOTONIC, #0x4, ne
179	b.ne	1f
180
181	ldr	x2, 5f
182	b	2f
1831:
184	cmp	w0, #CLOCK_REALTIME_COARSE
185	ccmp	w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
186	b.ne	4f
187	ldr	x2, 6f
1882:
189	cbz	w1, 3f
190	stp	xzr, x2, [x1]
191
1923:	/* res == NULL. */
193	mov	w0, wzr
194	ret
195
1964:	/* Syscall fallback. */
197	mov	x8, #__NR_clock_getres
198	svc	#0
199	ret
2005:
201	.quad	CLOCK_REALTIME_RES
2026:
203	.quad	CLOCK_COARSE_RES
204	.cfi_endproc
205ENDPROC(__kernel_clock_getres)
206
207/*
208 * Read the current time from the architected counter.
209 * Expects vdso_data to be initialised.
210 * Clobbers the temporary registers (x9 - x15).
211 * Returns:
212 *  - w9		= vDSO sequence counter
213 *  - (x10, x11)	= (ts->tv_sec, shifted ts->tv_nsec)
214 *  - w12		= cs_shift
215 */
216ENTRY(__do_get_tspec)
217	.cfi_startproc
218
219	/* Read from the vDSO data page. */
220	ldr	x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
221	ldp	x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
222	ldp	w11, w12, [vdso_data, #VDSO_CS_MULT]
223	seqcnt_read w9
224
225	/* Read the virtual counter. */
226	isb
227	mrs	x15, cntvct_el0
228
229	/* Calculate cycle delta and convert to ns. */
230	sub	x10, x15, x10
231	/* We can only guarantee 56 bits of precision. */
232	movn	x15, #0xff00, lsl #48
233	and	x10, x15, x10
234	mul	x10, x10, x11
235
236	/* Use the kernel time to calculate the new timespec. */
237	mov	x11, #NSEC_PER_SEC_LO16
238	movk	x11, #NSEC_PER_SEC_HI16, lsl #16
239	lsl	x11, x11, x12
240	add	x15, x10, x14
241	udiv	x14, x15, x11
242	add	x10, x13, x14
243	mul	x13, x14, x11
244	sub	x11, x15, x13
245
246	ret
247	.cfi_endproc
248ENDPROC(__do_get_tspec)
249