1 // SPDX-License-Identifier: GPL-2.0
3 * Real Time Clock Driver Test Program
5 * Copyright (c) 2018 Alexandre Belloni <alexandre.belloni@bootlin.com>
10 #include <linux/rtc.h>
13 #include <sys/ioctl.h>
15 #include <sys/types.h>
19 #include "../kselftest_harness.h"
24 static char *rtc_file
= "/dev/rtc0";
31 self
->fd
= open(rtc_file
, O_RDONLY
);
32 ASSERT_NE(-1, self
->fd
);
35 FIXTURE_TEARDOWN(rtc
) {
39 TEST_F(rtc
, date_read
) {
41 struct rtc_time rtc_tm
;
43 /* Read the RTC time/date */
44 rc
= ioctl(self
->fd
, RTC_RD_TIME
, &rtc_tm
);
47 TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
48 rtc_tm
.tm_mday
, rtc_tm
.tm_mon
+ 1, rtc_tm
.tm_year
+ 1900,
49 rtc_tm
.tm_hour
, rtc_tm
.tm_min
, rtc_tm
.tm_sec
);
52 TEST_F_TIMEOUT(rtc
, uie_read
, NUM_UIE
+ 2) {
56 /* Turn on update interrupts */
57 rc
= ioctl(self
->fd
, RTC_UIE_ON
, 0);
59 ASSERT_EQ(EINVAL
, errno
);
60 TH_LOG("skip update IRQs not supported.");
64 for (i
= 0; i
< NUM_UIE
; i
++) {
65 /* This read will block */
66 rc
= read(self
->fd
, &data
, sizeof(data
));
71 EXPECT_EQ(NUM_UIE
, irq
);
73 rc
= ioctl(self
->fd
, RTC_UIE_OFF
, 0);
77 TEST_F(rtc
, uie_select
) {
81 /* Turn on update interrupts */
82 rc
= ioctl(self
->fd
, RTC_UIE_ON
, 0);
84 ASSERT_EQ(EINVAL
, errno
);
85 TH_LOG("skip update IRQs not supported.");
89 for (i
= 0; i
< NUM_UIE
; i
++) {
90 struct timeval tv
= { .tv_sec
= 2 };
94 FD_SET(self
->fd
, &readfds
);
95 /* The select will wait until an RTC interrupt happens. */
96 rc
= select(self
->fd
+ 1, &readfds
, NULL
, NULL
, &tv
);
100 /* This read won't block */
101 rc
= read(self
->fd
, &data
, sizeof(unsigned long));
106 EXPECT_EQ(NUM_UIE
, irq
);
108 rc
= ioctl(self
->fd
, RTC_UIE_OFF
, 0);
112 TEST_F(rtc
, alarm_alm_set
) {
113 struct timeval tv
= { .tv_sec
= ALARM_DELTA
+ 2 };
120 rc
= ioctl(self
->fd
, RTC_RD_TIME
, &tm
);
123 secs
= timegm((struct tm
*)&tm
) + ALARM_DELTA
;
124 gmtime_r(&secs
, (struct tm
*)&tm
);
126 rc
= ioctl(self
->fd
, RTC_ALM_SET
, &tm
);
128 ASSERT_EQ(EINVAL
, errno
);
129 TH_LOG("skip alarms are not supported.");
133 rc
= ioctl(self
->fd
, RTC_ALM_READ
, &tm
);
136 TH_LOG("Alarm time now set to %02d:%02d:%02d.",
137 tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
139 /* Enable alarm interrupts */
140 rc
= ioctl(self
->fd
, RTC_AIE_ON
, 0);
144 FD_SET(self
->fd
, &readfds
);
146 rc
= select(self
->fd
+ 1, &readfds
, NULL
, NULL
, &tv
);
150 /* Disable alarm interrupts */
151 rc
= ioctl(self
->fd
, RTC_AIE_OFF
, 0);
154 rc
= read(self
->fd
, &data
, sizeof(unsigned long));
156 TH_LOG("data: %lx", data
);
158 rc
= ioctl(self
->fd
, RTC_RD_TIME
, &tm
);
161 new = timegm((struct tm
*)&tm
);
162 ASSERT_EQ(new, secs
);
165 TEST_F(rtc
, alarm_wkalm_set
) {
166 struct timeval tv
= { .tv_sec
= ALARM_DELTA
+ 2 };
167 struct rtc_wkalrm alarm
= { 0 };
174 rc
= ioctl(self
->fd
, RTC_RD_TIME
, &alarm
.time
);
177 secs
= timegm((struct tm
*)&alarm
.time
) + ALARM_DELTA
;
178 gmtime_r(&secs
, (struct tm
*)&alarm
.time
);
182 rc
= ioctl(self
->fd
, RTC_WKALM_SET
, &alarm
);
184 ASSERT_EQ(EINVAL
, errno
);
185 TH_LOG("skip alarms are not supported.");
189 rc
= ioctl(self
->fd
, RTC_WKALM_RD
, &alarm
);
192 TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
193 alarm
.time
.tm_mday
, alarm
.time
.tm_mon
+ 1,
194 alarm
.time
.tm_year
+ 1900, alarm
.time
.tm_hour
,
195 alarm
.time
.tm_min
, alarm
.time
.tm_sec
);
198 FD_SET(self
->fd
, &readfds
);
200 rc
= select(self
->fd
+ 1, &readfds
, NULL
, NULL
, &tv
);
204 rc
= read(self
->fd
, &data
, sizeof(unsigned long));
207 rc
= ioctl(self
->fd
, RTC_RD_TIME
, &tm
);
210 new = timegm((struct tm
*)&tm
);
211 ASSERT_EQ(new, secs
);
214 TEST_F_TIMEOUT(rtc
, alarm_alm_set_minute
, 65) {
215 struct timeval tv
= { .tv_sec
= 62 };
222 rc
= ioctl(self
->fd
, RTC_RD_TIME
, &tm
);
225 secs
= timegm((struct tm
*)&tm
) + 60 - tm
.tm_sec
;
226 gmtime_r(&secs
, (struct tm
*)&tm
);
228 rc
= ioctl(self
->fd
, RTC_ALM_SET
, &tm
);
230 ASSERT_EQ(EINVAL
, errno
);
231 TH_LOG("skip alarms are not supported.");
235 rc
= ioctl(self
->fd
, RTC_ALM_READ
, &tm
);
238 TH_LOG("Alarm time now set to %02d:%02d:%02d.",
239 tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
241 /* Enable alarm interrupts */
242 rc
= ioctl(self
->fd
, RTC_AIE_ON
, 0);
246 FD_SET(self
->fd
, &readfds
);
248 rc
= select(self
->fd
+ 1, &readfds
, NULL
, NULL
, &tv
);
252 /* Disable alarm interrupts */
253 rc
= ioctl(self
->fd
, RTC_AIE_OFF
, 0);
256 rc
= read(self
->fd
, &data
, sizeof(unsigned long));
258 TH_LOG("data: %lx", data
);
260 rc
= ioctl(self
->fd
, RTC_RD_TIME
, &tm
);
263 new = timegm((struct tm
*)&tm
);
264 ASSERT_EQ(new, secs
);
267 TEST_F_TIMEOUT(rtc
, alarm_wkalm_set_minute
, 65) {
268 struct timeval tv
= { .tv_sec
= 62 };
269 struct rtc_wkalrm alarm
= { 0 };
276 rc
= ioctl(self
->fd
, RTC_RD_TIME
, &alarm
.time
);
279 secs
= timegm((struct tm
*)&alarm
.time
) + 60 - alarm
.time
.tm_sec
;
280 gmtime_r(&secs
, (struct tm
*)&alarm
.time
);
284 rc
= ioctl(self
->fd
, RTC_WKALM_SET
, &alarm
);
286 ASSERT_EQ(EINVAL
, errno
);
287 TH_LOG("skip alarms are not supported.");
291 rc
= ioctl(self
->fd
, RTC_WKALM_RD
, &alarm
);
294 TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
295 alarm
.time
.tm_mday
, alarm
.time
.tm_mon
+ 1,
296 alarm
.time
.tm_year
+ 1900, alarm
.time
.tm_hour
,
297 alarm
.time
.tm_min
, alarm
.time
.tm_sec
);
300 FD_SET(self
->fd
, &readfds
);
302 rc
= select(self
->fd
+ 1, &readfds
, NULL
, NULL
, &tv
);
306 rc
= read(self
->fd
, &data
, sizeof(unsigned long));
309 rc
= ioctl(self
->fd
, RTC_RD_TIME
, &tm
);
312 new = timegm((struct tm
*)&tm
);
313 ASSERT_EQ(new, secs
);
316 static void __attribute__((constructor
))
317 __constructor_order_last(void)
319 if (!__constructor_order
)
320 __constructor_order
= _CONSTRUCTOR_ORDER_BACKWARD
;
323 int main(int argc
, char **argv
)
332 fprintf(stderr
, "usage: %s [rtcdev]\n", argv
[0]);
336 return test_harness_run(argc
, argv
);