1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Real Time Clock Driver Test Program
4 *
5 * Copyright (c) 2018 Alexandre Belloni <alexandre.belloni@bootlin.com>
6 */
7
8#include <errno.h>
9#include <fcntl.h>
10#include <linux/rtc.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <sys/ioctl.h>
14#include <sys/time.h>
15#include <sys/types.h>
16#include <time.h>
17#include <unistd.h>
18
19#include "../kselftest_harness.h"
20
21#define NUM_UIE 3
22#define ALARM_DELTA 3
23#define READ_LOOP_DURATION_SEC 30
24#define READ_LOOP_SLEEP_MS 11
25
26static char *rtc_file = "/dev/rtc0";
27
28FIXTURE(rtc) {
29	int fd;
30};
31
32FIXTURE_SETUP(rtc) {
33	self->fd = open(rtc_file, O_RDONLY);
34}
35
36FIXTURE_TEARDOWN(rtc) {
37	close(self->fd);
38}
39
40TEST_F(rtc, date_read) {
41	int rc;
42	struct rtc_time rtc_tm;
43
44	if (self->fd == -1 && errno == ENOENT)
45		SKIP(return, "Skipping test since %s does not exist", rtc_file);
46	ASSERT_NE(-1, self->fd);
47
48	/* Read the RTC time/date */
49	rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
50	ASSERT_NE(-1, rc);
51
52	TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
53	       rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
54	       rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
55}
56
57static time_t rtc_time_to_timestamp(struct rtc_time *rtc_time)
58{
59	struct tm tm_time = {
60	       .tm_sec = rtc_time->tm_sec,
61	       .tm_min = rtc_time->tm_min,
62	       .tm_hour = rtc_time->tm_hour,
63	       .tm_mday = rtc_time->tm_mday,
64	       .tm_mon = rtc_time->tm_mon,
65	       .tm_year = rtc_time->tm_year,
66	};
67
68	return mktime(&tm_time);
69}
70
71static void nanosleep_with_retries(long ns)
72{
73	struct timespec req = {
74		.tv_sec = 0,
75		.tv_nsec = ns,
76	};
77	struct timespec rem;
78
79	while (nanosleep(&req, &rem) != 0) {
80		req.tv_sec = rem.tv_sec;
81		req.tv_nsec = rem.tv_nsec;
82	}
83}
84
85TEST_F_TIMEOUT(rtc, date_read_loop, READ_LOOP_DURATION_SEC + 2) {
86	int rc;
87	long iter_count = 0;
88	struct rtc_time rtc_tm;
89	time_t start_rtc_read, prev_rtc_read;
90
91	if (self->fd == -1 && errno == ENOENT)
92		SKIP(return, "Skipping test since %s does not exist", rtc_file);
93	ASSERT_NE(-1, self->fd);
94
95	TH_LOG("Continuously reading RTC time for %ds (with %dms breaks after every read).",
96	       READ_LOOP_DURATION_SEC, READ_LOOP_SLEEP_MS);
97
98	rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
99	ASSERT_NE(-1, rc);
100	start_rtc_read = rtc_time_to_timestamp(&rtc_tm);
101	prev_rtc_read = start_rtc_read;
102
103	do  {
104		time_t rtc_read;
105
106		rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
107		ASSERT_NE(-1, rc);
108
109		rtc_read = rtc_time_to_timestamp(&rtc_tm);
110		/* Time should not go backwards */
111		ASSERT_LE(prev_rtc_read, rtc_read);
112		/* Time should not increase more then 1s at a time */
113		ASSERT_GE(prev_rtc_read + 1, rtc_read);
114
115		/* Sleep 11ms to avoid killing / overheating the RTC */
116		nanosleep_with_retries(READ_LOOP_SLEEP_MS * 1000000);
117
118		prev_rtc_read = rtc_read;
119		iter_count++;
120	} while (prev_rtc_read <= start_rtc_read + READ_LOOP_DURATION_SEC);
121
122	TH_LOG("Performed %ld RTC time reads.", iter_count);
123}
124
125TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
126	int i, rc, irq = 0;
127	unsigned long data;
128
129	if (self->fd == -1 && errno == ENOENT)
130		SKIP(return, "Skipping test since %s does not exist", rtc_file);
131	ASSERT_NE(-1, self->fd);
132
133	/* Turn on update interrupts */
134	rc = ioctl(self->fd, RTC_UIE_ON, 0);
135	if (rc == -1) {
136		ASSERT_EQ(EINVAL, errno);
137		TH_LOG("skip update IRQs not supported.");
138		return;
139	}
140
141	for (i = 0; i < NUM_UIE; i++) {
142		/* This read will block */
143		rc = read(self->fd, &data, sizeof(data));
144		ASSERT_NE(-1, rc);
145		irq++;
146	}
147
148	EXPECT_EQ(NUM_UIE, irq);
149
150	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
151	ASSERT_NE(-1, rc);
152}
153
154TEST_F(rtc, uie_select) {
155	int i, rc, irq = 0;
156	unsigned long data;
157
158	if (self->fd == -1 && errno == ENOENT)
159		SKIP(return, "Skipping test since %s does not exist", rtc_file);
160	ASSERT_NE(-1, self->fd);
161
162	/* Turn on update interrupts */
163	rc = ioctl(self->fd, RTC_UIE_ON, 0);
164	if (rc == -1) {
165		ASSERT_EQ(EINVAL, errno);
166		TH_LOG("skip update IRQs not supported.");
167		return;
168	}
169
170	for (i = 0; i < NUM_UIE; i++) {
171		struct timeval tv = { .tv_sec = 2 };
172		fd_set readfds;
173
174		FD_ZERO(&readfds);
175		FD_SET(self->fd, &readfds);
176		/* The select will wait until an RTC interrupt happens. */
177		rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
178		ASSERT_NE(-1, rc);
179		ASSERT_NE(0, rc);
180
181		/* This read won't block */
182		rc = read(self->fd, &data, sizeof(unsigned long));
183		ASSERT_NE(-1, rc);
184		irq++;
185	}
186
187	EXPECT_EQ(NUM_UIE, irq);
188
189	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
190	ASSERT_NE(-1, rc);
191}
192
193TEST_F(rtc, alarm_alm_set) {
194	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
195	unsigned long data;
196	struct rtc_time tm;
197	fd_set readfds;
198	time_t secs, new;
199	int rc;
200
201	if (self->fd == -1 && errno == ENOENT)
202		SKIP(return, "Skipping test since %s does not exist", rtc_file);
203	ASSERT_NE(-1, self->fd);
204
205	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
206	ASSERT_NE(-1, rc);
207
208	secs = timegm((struct tm *)&tm) + ALARM_DELTA;
209	gmtime_r(&secs, (struct tm *)&tm);
210
211	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
212	if (rc == -1) {
213		ASSERT_EQ(EINVAL, errno);
214		TH_LOG("skip alarms are not supported.");
215		return;
216	}
217
218	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
219	ASSERT_NE(-1, rc);
220
221	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
222	       tm.tm_hour, tm.tm_min, tm.tm_sec);
223
224	/* Enable alarm interrupts */
225	rc = ioctl(self->fd, RTC_AIE_ON, 0);
226	ASSERT_NE(-1, rc);
227
228	FD_ZERO(&readfds);
229	FD_SET(self->fd, &readfds);
230
231	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
232	ASSERT_NE(-1, rc);
233	ASSERT_NE(0, rc);
234
235	/* Disable alarm interrupts */
236	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
237	ASSERT_NE(-1, rc);
238
239	rc = read(self->fd, &data, sizeof(unsigned long));
240	ASSERT_NE(-1, rc);
241	TH_LOG("data: %lx", data);
242
243	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
244	ASSERT_NE(-1, rc);
245
246	new = timegm((struct tm *)&tm);
247	ASSERT_EQ(new, secs);
248}
249
250TEST_F(rtc, alarm_wkalm_set) {
251	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
252	struct rtc_wkalrm alarm = { 0 };
253	struct rtc_time tm;
254	unsigned long data;
255	fd_set readfds;
256	time_t secs, new;
257	int rc;
258
259	if (self->fd == -1 && errno == ENOENT)
260		SKIP(return, "Skipping test since %s does not exist", rtc_file);
261	ASSERT_NE(-1, self->fd);
262
263	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
264	ASSERT_NE(-1, rc);
265
266	secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
267	gmtime_r(&secs, (struct tm *)&alarm.time);
268
269	alarm.enabled = 1;
270
271	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
272	if (rc == -1) {
273		ASSERT_EQ(EINVAL, errno);
274		TH_LOG("skip alarms are not supported.");
275		return;
276	}
277
278	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
279	ASSERT_NE(-1, rc);
280
281	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
282	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
283	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
284	       alarm.time.tm_min, alarm.time.tm_sec);
285
286	FD_ZERO(&readfds);
287	FD_SET(self->fd, &readfds);
288
289	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
290	ASSERT_NE(-1, rc);
291	ASSERT_NE(0, rc);
292
293	rc = read(self->fd, &data, sizeof(unsigned long));
294	ASSERT_NE(-1, rc);
295
296	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
297	ASSERT_NE(-1, rc);
298
299	new = timegm((struct tm *)&tm);
300	ASSERT_EQ(new, secs);
301}
302
303TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
304	struct timeval tv = { .tv_sec = 62 };
305	unsigned long data;
306	struct rtc_time tm;
307	fd_set readfds;
308	time_t secs, new;
309	int rc;
310
311	if (self->fd == -1 && errno == ENOENT)
312		SKIP(return, "Skipping test since %s does not exist", rtc_file);
313	ASSERT_NE(-1, self->fd);
314
315	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
316	ASSERT_NE(-1, rc);
317
318	secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec;
319	gmtime_r(&secs, (struct tm *)&tm);
320
321	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
322	if (rc == -1) {
323		ASSERT_EQ(EINVAL, errno);
324		TH_LOG("skip alarms are not supported.");
325		return;
326	}
327
328	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
329	ASSERT_NE(-1, rc);
330
331	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
332	       tm.tm_hour, tm.tm_min, tm.tm_sec);
333
334	/* Enable alarm interrupts */
335	rc = ioctl(self->fd, RTC_AIE_ON, 0);
336	ASSERT_NE(-1, rc);
337
338	FD_ZERO(&readfds);
339	FD_SET(self->fd, &readfds);
340
341	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
342	ASSERT_NE(-1, rc);
343	ASSERT_NE(0, rc);
344
345	/* Disable alarm interrupts */
346	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
347	ASSERT_NE(-1, rc);
348
349	rc = read(self->fd, &data, sizeof(unsigned long));
350	ASSERT_NE(-1, rc);
351	TH_LOG("data: %lx", data);
352
353	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
354	ASSERT_NE(-1, rc);
355
356	new = timegm((struct tm *)&tm);
357	ASSERT_EQ(new, secs);
358}
359
360TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
361	struct timeval tv = { .tv_sec = 62 };
362	struct rtc_wkalrm alarm = { 0 };
363	struct rtc_time tm;
364	unsigned long data;
365	fd_set readfds;
366	time_t secs, new;
367	int rc;
368
369	if (self->fd == -1 && errno == ENOENT)
370		SKIP(return, "Skipping test since %s does not exist", rtc_file);
371	ASSERT_NE(-1, self->fd);
372
373	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
374	ASSERT_NE(-1, rc);
375
376	secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec;
377	gmtime_r(&secs, (struct tm *)&alarm.time);
378
379	alarm.enabled = 1;
380
381	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
382	if (rc == -1) {
383		ASSERT_EQ(EINVAL, errno);
384		TH_LOG("skip alarms are not supported.");
385		return;
386	}
387
388	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
389	ASSERT_NE(-1, rc);
390
391	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
392	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
393	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
394	       alarm.time.tm_min, alarm.time.tm_sec);
395
396	FD_ZERO(&readfds);
397	FD_SET(self->fd, &readfds);
398
399	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
400	ASSERT_NE(-1, rc);
401	ASSERT_NE(0, rc);
402
403	rc = read(self->fd, &data, sizeof(unsigned long));
404	ASSERT_NE(-1, rc);
405
406	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
407	ASSERT_NE(-1, rc);
408
409	new = timegm((struct tm *)&tm);
410	ASSERT_EQ(new, secs);
411}
412
413static void __attribute__((constructor))
414__constructor_order_last(void)
415{
416	if (!__constructor_order)
417		__constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
418}
419
420int main(int argc, char **argv)
421{
422	switch (argc) {
423	case 2:
424		rtc_file = argv[1];
425		/* FALLTHROUGH */
426	case 1:
427		break;
428	default:
429		fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
430		return 1;
431	}
432
433	return test_harness_run(argc, argv);
434}
435