1d8da8665SAlexandre Belloni // SPDX-License-Identifier: GPL-2.0 2a12ab9e1SAlexandre Belloni /* 3d8da8665SAlexandre Belloni * Real Time Clock Driver Test Program 4a12ab9e1SAlexandre Belloni * 5d8da8665SAlexandre Belloni * Copyright (c) 2018 Alexandre Belloni <alexandre.belloni@bootlin.com> 6a12ab9e1SAlexandre Belloni */ 7a12ab9e1SAlexandre Belloni 8d8da8665SAlexandre Belloni #include <errno.h> 9d8da8665SAlexandre Belloni #include <fcntl.h> 10a12ab9e1SAlexandre Belloni #include <linux/rtc.h> 11d8da8665SAlexandre Belloni #include <stdio.h> 12d8da8665SAlexandre Belloni #include <stdlib.h> 13a12ab9e1SAlexandre Belloni #include <sys/ioctl.h> 14a12ab9e1SAlexandre Belloni #include <sys/time.h> 15a12ab9e1SAlexandre Belloni #include <sys/types.h> 16d8da8665SAlexandre Belloni #include <time.h> 17a12ab9e1SAlexandre Belloni #include <unistd.h> 18a12ab9e1SAlexandre Belloni 19d8da8665SAlexandre Belloni #include "../kselftest_harness.h" 20a12ab9e1SAlexandre Belloni 21d8da8665SAlexandre Belloni #define NUM_UIE 3 22d8da8665SAlexandre Belloni #define ALARM_DELTA 3 23a12ab9e1SAlexandre Belloni 24d8da8665SAlexandre Belloni static char *rtc_file = "/dev/rtc0"; 25d8da8665SAlexandre Belloni 26d8da8665SAlexandre Belloni FIXTURE(rtc) { 27d8da8665SAlexandre Belloni int fd; 28a12ab9e1SAlexandre Belloni }; 29a12ab9e1SAlexandre Belloni 30d8da8665SAlexandre Belloni FIXTURE_SETUP(rtc) { 31d8da8665SAlexandre Belloni self->fd = open(rtc_file, O_RDONLY); 32d8da8665SAlexandre Belloni ASSERT_NE(-1, self->fd); 33d8da8665SAlexandre Belloni } 34a12ab9e1SAlexandre Belloni 35d8da8665SAlexandre Belloni FIXTURE_TEARDOWN(rtc) { 36d8da8665SAlexandre Belloni close(self->fd); 37d8da8665SAlexandre Belloni } 38d8da8665SAlexandre Belloni 39d8da8665SAlexandre Belloni TEST_F(rtc, date_read) { 40d8da8665SAlexandre Belloni int rc; 41d8da8665SAlexandre Belloni struct rtc_time rtc_tm; 42d8da8665SAlexandre Belloni 43d8da8665SAlexandre Belloni /* Read the RTC time/date */ 44d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm); 45d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 46d8da8665SAlexandre Belloni 47d8da8665SAlexandre Belloni TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.", 48d8da8665SAlexandre Belloni rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900, 49d8da8665SAlexandre Belloni rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); 50d8da8665SAlexandre Belloni } 51d8da8665SAlexandre Belloni 52d8da8665SAlexandre Belloni TEST_F(rtc, uie_read) { 53d8da8665SAlexandre Belloni int i, rc, irq = 0; 54d8da8665SAlexandre Belloni unsigned long data; 55d8da8665SAlexandre Belloni 56d8da8665SAlexandre Belloni /* Turn on update interrupts */ 57d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_UIE_ON, 0); 58d8da8665SAlexandre Belloni if (rc == -1) { 59d8da8665SAlexandre Belloni ASSERT_EQ(EINVAL, errno); 60d8da8665SAlexandre Belloni TH_LOG("skip update IRQs not supported."); 61d8da8665SAlexandre Belloni return; 62d8da8665SAlexandre Belloni } 63d8da8665SAlexandre Belloni 64d8da8665SAlexandre Belloni for (i = 0; i < NUM_UIE; i++) { 65d8da8665SAlexandre Belloni /* This read will block */ 66d8da8665SAlexandre Belloni rc = read(self->fd, &data, sizeof(data)); 67d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 68d8da8665SAlexandre Belloni irq++; 69d8da8665SAlexandre Belloni } 70d8da8665SAlexandre Belloni 71d8da8665SAlexandre Belloni EXPECT_EQ(NUM_UIE, irq); 72d8da8665SAlexandre Belloni 73d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_UIE_OFF, 0); 74d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 75d8da8665SAlexandre Belloni } 76d8da8665SAlexandre Belloni 77d8da8665SAlexandre Belloni TEST_F(rtc, uie_select) { 78d8da8665SAlexandre Belloni int i, rc, irq = 0; 79d8da8665SAlexandre Belloni unsigned long data; 80d8da8665SAlexandre Belloni 81d8da8665SAlexandre Belloni /* Turn on update interrupts */ 82d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_UIE_ON, 0); 83d8da8665SAlexandre Belloni if (rc == -1) { 84d8da8665SAlexandre Belloni ASSERT_EQ(EINVAL, errno); 85d8da8665SAlexandre Belloni TH_LOG("skip update IRQs not supported."); 86d8da8665SAlexandre Belloni return; 87d8da8665SAlexandre Belloni } 88d8da8665SAlexandre Belloni 89d8da8665SAlexandre Belloni for (i = 0; i < NUM_UIE; i++) { 90d8da8665SAlexandre Belloni struct timeval tv = { .tv_sec = 2 }; 91d8da8665SAlexandre Belloni fd_set readfds; 92d8da8665SAlexandre Belloni 93d8da8665SAlexandre Belloni FD_ZERO(&readfds); 94d8da8665SAlexandre Belloni FD_SET(self->fd, &readfds); 95d8da8665SAlexandre Belloni /* The select will wait until an RTC interrupt happens. */ 96d8da8665SAlexandre Belloni rc = select(self->fd + 1, &readfds, NULL, NULL, &tv); 97d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 98d8da8665SAlexandre Belloni ASSERT_NE(0, rc); 99d8da8665SAlexandre Belloni 100d8da8665SAlexandre Belloni /* This read won't block */ 101d8da8665SAlexandre Belloni rc = read(self->fd, &data, sizeof(unsigned long)); 102d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 103d8da8665SAlexandre Belloni irq++; 104d8da8665SAlexandre Belloni } 105d8da8665SAlexandre Belloni 106d8da8665SAlexandre Belloni EXPECT_EQ(NUM_UIE, irq); 107d8da8665SAlexandre Belloni 108d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_UIE_OFF, 0); 109d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 110d8da8665SAlexandre Belloni } 111d8da8665SAlexandre Belloni 112d8da8665SAlexandre Belloni TEST_F(rtc, alarm_alm_set) { 113d8da8665SAlexandre Belloni struct timeval tv = { .tv_sec = ALARM_DELTA + 2 }; 114d8da8665SAlexandre Belloni unsigned long data; 115d8da8665SAlexandre Belloni struct rtc_time tm; 116d8da8665SAlexandre Belloni fd_set readfds; 117d8da8665SAlexandre Belloni time_t secs, new; 118d8da8665SAlexandre Belloni int rc; 119d8da8665SAlexandre Belloni 120d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &tm); 121d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 122d8da8665SAlexandre Belloni 123d8da8665SAlexandre Belloni secs = timegm((struct tm *)&tm) + ALARM_DELTA; 124d8da8665SAlexandre Belloni gmtime_r(&secs, (struct tm *)&tm); 125d8da8665SAlexandre Belloni 126d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_ALM_SET, &tm); 127d8da8665SAlexandre Belloni if (rc == -1) { 128d8da8665SAlexandre Belloni ASSERT_EQ(EINVAL, errno); 129d8da8665SAlexandre Belloni TH_LOG("skip alarms are not supported."); 130d8da8665SAlexandre Belloni return; 131d8da8665SAlexandre Belloni } 132d8da8665SAlexandre Belloni 133d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_ALM_READ, &tm); 134d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 135d8da8665SAlexandre Belloni 136d8da8665SAlexandre Belloni TH_LOG("Alarm time now set to %02d:%02d:%02d.", 137d8da8665SAlexandre Belloni tm.tm_hour, tm.tm_min, tm.tm_sec); 138d8da8665SAlexandre Belloni 139d8da8665SAlexandre Belloni /* Enable alarm interrupts */ 140d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_AIE_ON, 0); 141d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 142d8da8665SAlexandre Belloni 143d8da8665SAlexandre Belloni FD_ZERO(&readfds); 144d8da8665SAlexandre Belloni FD_SET(self->fd, &readfds); 145d8da8665SAlexandre Belloni 146d8da8665SAlexandre Belloni rc = select(self->fd + 1, &readfds, NULL, NULL, &tv); 147d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 148fdac9448SAlexandre Belloni ASSERT_NE(0, rc); 149d8da8665SAlexandre Belloni 150d8da8665SAlexandre Belloni /* Disable alarm interrupts */ 151d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_AIE_OFF, 0); 152d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 153d8da8665SAlexandre Belloni 154d8da8665SAlexandre Belloni rc = read(self->fd, &data, sizeof(unsigned long)); 155d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 156d8da8665SAlexandre Belloni TH_LOG("data: %lx", data); 157d8da8665SAlexandre Belloni 158d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &tm); 159d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 160d8da8665SAlexandre Belloni 161d8da8665SAlexandre Belloni new = timegm((struct tm *)&tm); 162d8da8665SAlexandre Belloni ASSERT_EQ(new, secs); 163d8da8665SAlexandre Belloni } 164d8da8665SAlexandre Belloni 165d8da8665SAlexandre Belloni TEST_F(rtc, alarm_wkalm_set) { 166d8da8665SAlexandre Belloni struct timeval tv = { .tv_sec = ALARM_DELTA + 2 }; 167d8da8665SAlexandre Belloni struct rtc_wkalrm alarm = { 0 }; 168d8da8665SAlexandre Belloni struct rtc_time tm; 169d8da8665SAlexandre Belloni unsigned long data; 170d8da8665SAlexandre Belloni fd_set readfds; 171d8da8665SAlexandre Belloni time_t secs, new; 172d8da8665SAlexandre Belloni int rc; 173d8da8665SAlexandre Belloni 174d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time); 175d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 176d8da8665SAlexandre Belloni 177d8da8665SAlexandre Belloni secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA; 178d8da8665SAlexandre Belloni gmtime_r(&secs, (struct tm *)&alarm.time); 179d8da8665SAlexandre Belloni 180d8da8665SAlexandre Belloni alarm.enabled = 1; 181d8da8665SAlexandre Belloni 182d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_WKALM_SET, &alarm); 183d8da8665SAlexandre Belloni if (rc == -1) { 184d8da8665SAlexandre Belloni ASSERT_EQ(EINVAL, errno); 185d8da8665SAlexandre Belloni TH_LOG("skip alarms are not supported."); 186d8da8665SAlexandre Belloni return; 187d8da8665SAlexandre Belloni } 188d8da8665SAlexandre Belloni 189d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_WKALM_RD, &alarm); 190d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 191d8da8665SAlexandre Belloni 192d8da8665SAlexandre Belloni TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.", 193d8da8665SAlexandre Belloni alarm.time.tm_mday, alarm.time.tm_mon + 1, 194d8da8665SAlexandre Belloni alarm.time.tm_year + 1900, alarm.time.tm_hour, 195d8da8665SAlexandre Belloni alarm.time.tm_min, alarm.time.tm_sec); 196d8da8665SAlexandre Belloni 197d8da8665SAlexandre Belloni FD_ZERO(&readfds); 198d8da8665SAlexandre Belloni FD_SET(self->fd, &readfds); 199d8da8665SAlexandre Belloni 200d8da8665SAlexandre Belloni rc = select(self->fd + 1, &readfds, NULL, NULL, &tv); 201d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 202fdac9448SAlexandre Belloni ASSERT_NE(0, rc); 203d8da8665SAlexandre Belloni 204d8da8665SAlexandre Belloni rc = read(self->fd, &data, sizeof(unsigned long)); 205d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 206d8da8665SAlexandre Belloni 207d8da8665SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &tm); 208d8da8665SAlexandre Belloni ASSERT_NE(-1, rc); 209d8da8665SAlexandre Belloni 210d8da8665SAlexandre Belloni new = timegm((struct tm *)&tm); 211d8da8665SAlexandre Belloni ASSERT_EQ(new, secs); 212d8da8665SAlexandre Belloni } 213d8da8665SAlexandre Belloni 214*7b302772SAlexandre Belloni TEST_F(rtc, alarm_alm_set_minute) { 215*7b302772SAlexandre Belloni struct timeval tv = { .tv_sec = 62 }; 216*7b302772SAlexandre Belloni unsigned long data; 217*7b302772SAlexandre Belloni struct rtc_time tm; 218*7b302772SAlexandre Belloni fd_set readfds; 219*7b302772SAlexandre Belloni time_t secs, new; 220*7b302772SAlexandre Belloni int rc; 221*7b302772SAlexandre Belloni 222*7b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &tm); 223*7b302772SAlexandre Belloni ASSERT_NE(-1, rc); 224*7b302772SAlexandre Belloni 225*7b302772SAlexandre Belloni secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec; 226*7b302772SAlexandre Belloni gmtime_r(&secs, (struct tm *)&tm); 227*7b302772SAlexandre Belloni 228*7b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_ALM_SET, &tm); 229*7b302772SAlexandre Belloni if (rc == -1) { 230*7b302772SAlexandre Belloni ASSERT_EQ(EINVAL, errno); 231*7b302772SAlexandre Belloni TH_LOG("skip alarms are not supported."); 232*7b302772SAlexandre Belloni return; 233*7b302772SAlexandre Belloni } 234*7b302772SAlexandre Belloni 235*7b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_ALM_READ, &tm); 236*7b302772SAlexandre Belloni ASSERT_NE(-1, rc); 237*7b302772SAlexandre Belloni 238*7b302772SAlexandre Belloni TH_LOG("Alarm time now set to %02d:%02d:%02d.", 239*7b302772SAlexandre Belloni tm.tm_hour, tm.tm_min, tm.tm_sec); 240*7b302772SAlexandre Belloni 241*7b302772SAlexandre Belloni /* Enable alarm interrupts */ 242*7b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_AIE_ON, 0); 243*7b302772SAlexandre Belloni ASSERT_NE(-1, rc); 244*7b302772SAlexandre Belloni 245*7b302772SAlexandre Belloni FD_ZERO(&readfds); 246*7b302772SAlexandre Belloni FD_SET(self->fd, &readfds); 247*7b302772SAlexandre Belloni 248*7b302772SAlexandre Belloni rc = select(self->fd + 1, &readfds, NULL, NULL, &tv); 249*7b302772SAlexandre Belloni ASSERT_NE(-1, rc); 250*7b302772SAlexandre Belloni ASSERT_NE(0, rc); 251*7b302772SAlexandre Belloni 252*7b302772SAlexandre Belloni /* Disable alarm interrupts */ 253*7b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_AIE_OFF, 0); 254*7b302772SAlexandre Belloni ASSERT_NE(-1, rc); 255*7b302772SAlexandre Belloni 256*7b302772SAlexandre Belloni rc = read(self->fd, &data, sizeof(unsigned long)); 257*7b302772SAlexandre Belloni ASSERT_NE(-1, rc); 258*7b302772SAlexandre Belloni TH_LOG("data: %lx", data); 259*7b302772SAlexandre Belloni 260*7b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &tm); 261*7b302772SAlexandre Belloni ASSERT_NE(-1, rc); 262*7b302772SAlexandre Belloni 263*7b302772SAlexandre Belloni new = timegm((struct tm *)&tm); 264*7b302772SAlexandre Belloni ASSERT_EQ(new, secs); 265*7b302772SAlexandre Belloni } 266*7b302772SAlexandre Belloni 267*7b302772SAlexandre Belloni TEST_F(rtc, alarm_wkalm_set_minute) { 268*7b302772SAlexandre Belloni struct timeval tv = { .tv_sec = 62 }; 269*7b302772SAlexandre Belloni struct rtc_wkalrm alarm = { 0 }; 270*7b302772SAlexandre Belloni struct rtc_time tm; 271*7b302772SAlexandre Belloni unsigned long data; 272*7b302772SAlexandre Belloni fd_set readfds; 273*7b302772SAlexandre Belloni time_t secs, new; 274*7b302772SAlexandre Belloni int rc; 275*7b302772SAlexandre Belloni 276*7b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time); 277*7b302772SAlexandre Belloni ASSERT_NE(-1, rc); 278*7b302772SAlexandre Belloni 279*7b302772SAlexandre Belloni secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec; 280*7b302772SAlexandre Belloni gmtime_r(&secs, (struct tm *)&alarm.time); 281*7b302772SAlexandre Belloni 282*7b302772SAlexandre Belloni alarm.enabled = 1; 283*7b302772SAlexandre Belloni 284*7b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_WKALM_SET, &alarm); 285*7b302772SAlexandre Belloni if (rc == -1) { 286*7b302772SAlexandre Belloni ASSERT_EQ(EINVAL, errno); 287*7b302772SAlexandre Belloni TH_LOG("skip alarms are not supported."); 288*7b302772SAlexandre Belloni return; 289*7b302772SAlexandre Belloni } 290*7b302772SAlexandre Belloni 291*7b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_WKALM_RD, &alarm); 292*7b302772SAlexandre Belloni ASSERT_NE(-1, rc); 293*7b302772SAlexandre Belloni 294*7b302772SAlexandre Belloni TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.", 295*7b302772SAlexandre Belloni alarm.time.tm_mday, alarm.time.tm_mon + 1, 296*7b302772SAlexandre Belloni alarm.time.tm_year + 1900, alarm.time.tm_hour, 297*7b302772SAlexandre Belloni alarm.time.tm_min, alarm.time.tm_sec); 298*7b302772SAlexandre Belloni 299*7b302772SAlexandre Belloni FD_ZERO(&readfds); 300*7b302772SAlexandre Belloni FD_SET(self->fd, &readfds); 301*7b302772SAlexandre Belloni 302*7b302772SAlexandre Belloni rc = select(self->fd + 1, &readfds, NULL, NULL, &tv); 303*7b302772SAlexandre Belloni ASSERT_NE(-1, rc); 304*7b302772SAlexandre Belloni ASSERT_NE(0, rc); 305*7b302772SAlexandre Belloni 306*7b302772SAlexandre Belloni rc = read(self->fd, &data, sizeof(unsigned long)); 307*7b302772SAlexandre Belloni ASSERT_NE(-1, rc); 308*7b302772SAlexandre Belloni 309*7b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &tm); 310*7b302772SAlexandre Belloni ASSERT_NE(-1, rc); 311*7b302772SAlexandre Belloni 312*7b302772SAlexandre Belloni new = timegm((struct tm *)&tm); 313*7b302772SAlexandre Belloni ASSERT_EQ(new, secs); 314*7b302772SAlexandre Belloni } 315*7b302772SAlexandre Belloni 316d8da8665SAlexandre Belloni static void __attribute__((constructor)) 317d8da8665SAlexandre Belloni __constructor_order_last(void) 318d8da8665SAlexandre Belloni { 319d8da8665SAlexandre Belloni if (!__constructor_order) 320d8da8665SAlexandre Belloni __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD; 321a12ab9e1SAlexandre Belloni } 322a12ab9e1SAlexandre Belloni 323a12ab9e1SAlexandre Belloni int main(int argc, char **argv) 324a12ab9e1SAlexandre Belloni { 325a12ab9e1SAlexandre Belloni switch (argc) { 326a12ab9e1SAlexandre Belloni case 2: 327d8da8665SAlexandre Belloni rtc_file = argv[1]; 328a12ab9e1SAlexandre Belloni /* FALLTHROUGH */ 329a12ab9e1SAlexandre Belloni case 1: 330a12ab9e1SAlexandre Belloni break; 331a12ab9e1SAlexandre Belloni default: 332d8da8665SAlexandre Belloni fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]); 333a12ab9e1SAlexandre Belloni return 1; 334a12ab9e1SAlexandre Belloni } 335a12ab9e1SAlexandre Belloni 336d8da8665SAlexandre Belloni return test_harness_run(argc, argv); 337a12ab9e1SAlexandre Belloni } 338