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 52*eff82a26SAlexandre Belloni TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) { 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*eff82a26SAlexandre Belloni TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) { 2157b302772SAlexandre Belloni struct timeval tv = { .tv_sec = 62 }; 2167b302772SAlexandre Belloni unsigned long data; 2177b302772SAlexandre Belloni struct rtc_time tm; 2187b302772SAlexandre Belloni fd_set readfds; 2197b302772SAlexandre Belloni time_t secs, new; 2207b302772SAlexandre Belloni int rc; 2217b302772SAlexandre Belloni 2227b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &tm); 2237b302772SAlexandre Belloni ASSERT_NE(-1, rc); 2247b302772SAlexandre Belloni 2257b302772SAlexandre Belloni secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec; 2267b302772SAlexandre Belloni gmtime_r(&secs, (struct tm *)&tm); 2277b302772SAlexandre Belloni 2287b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_ALM_SET, &tm); 2297b302772SAlexandre Belloni if (rc == -1) { 2307b302772SAlexandre Belloni ASSERT_EQ(EINVAL, errno); 2317b302772SAlexandre Belloni TH_LOG("skip alarms are not supported."); 2327b302772SAlexandre Belloni return; 2337b302772SAlexandre Belloni } 2347b302772SAlexandre Belloni 2357b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_ALM_READ, &tm); 2367b302772SAlexandre Belloni ASSERT_NE(-1, rc); 2377b302772SAlexandre Belloni 2387b302772SAlexandre Belloni TH_LOG("Alarm time now set to %02d:%02d:%02d.", 2397b302772SAlexandre Belloni tm.tm_hour, tm.tm_min, tm.tm_sec); 2407b302772SAlexandre Belloni 2417b302772SAlexandre Belloni /* Enable alarm interrupts */ 2427b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_AIE_ON, 0); 2437b302772SAlexandre Belloni ASSERT_NE(-1, rc); 2447b302772SAlexandre Belloni 2457b302772SAlexandre Belloni FD_ZERO(&readfds); 2467b302772SAlexandre Belloni FD_SET(self->fd, &readfds); 2477b302772SAlexandre Belloni 2487b302772SAlexandre Belloni rc = select(self->fd + 1, &readfds, NULL, NULL, &tv); 2497b302772SAlexandre Belloni ASSERT_NE(-1, rc); 2507b302772SAlexandre Belloni ASSERT_NE(0, rc); 2517b302772SAlexandre Belloni 2527b302772SAlexandre Belloni /* Disable alarm interrupts */ 2537b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_AIE_OFF, 0); 2547b302772SAlexandre Belloni ASSERT_NE(-1, rc); 2557b302772SAlexandre Belloni 2567b302772SAlexandre Belloni rc = read(self->fd, &data, sizeof(unsigned long)); 2577b302772SAlexandre Belloni ASSERT_NE(-1, rc); 2587b302772SAlexandre Belloni TH_LOG("data: %lx", data); 2597b302772SAlexandre Belloni 2607b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &tm); 2617b302772SAlexandre Belloni ASSERT_NE(-1, rc); 2627b302772SAlexandre Belloni 2637b302772SAlexandre Belloni new = timegm((struct tm *)&tm); 2647b302772SAlexandre Belloni ASSERT_EQ(new, secs); 2657b302772SAlexandre Belloni } 2667b302772SAlexandre Belloni 267*eff82a26SAlexandre Belloni TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) { 2687b302772SAlexandre Belloni struct timeval tv = { .tv_sec = 62 }; 2697b302772SAlexandre Belloni struct rtc_wkalrm alarm = { 0 }; 2707b302772SAlexandre Belloni struct rtc_time tm; 2717b302772SAlexandre Belloni unsigned long data; 2727b302772SAlexandre Belloni fd_set readfds; 2737b302772SAlexandre Belloni time_t secs, new; 2747b302772SAlexandre Belloni int rc; 2757b302772SAlexandre Belloni 2767b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time); 2777b302772SAlexandre Belloni ASSERT_NE(-1, rc); 2787b302772SAlexandre Belloni 2797b302772SAlexandre Belloni secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec; 2807b302772SAlexandre Belloni gmtime_r(&secs, (struct tm *)&alarm.time); 2817b302772SAlexandre Belloni 2827b302772SAlexandre Belloni alarm.enabled = 1; 2837b302772SAlexandre Belloni 2847b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_WKALM_SET, &alarm); 2857b302772SAlexandre Belloni if (rc == -1) { 2867b302772SAlexandre Belloni ASSERT_EQ(EINVAL, errno); 2877b302772SAlexandre Belloni TH_LOG("skip alarms are not supported."); 2887b302772SAlexandre Belloni return; 2897b302772SAlexandre Belloni } 2907b302772SAlexandre Belloni 2917b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_WKALM_RD, &alarm); 2927b302772SAlexandre Belloni ASSERT_NE(-1, rc); 2937b302772SAlexandre Belloni 2947b302772SAlexandre Belloni TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.", 2957b302772SAlexandre Belloni alarm.time.tm_mday, alarm.time.tm_mon + 1, 2967b302772SAlexandre Belloni alarm.time.tm_year + 1900, alarm.time.tm_hour, 2977b302772SAlexandre Belloni alarm.time.tm_min, alarm.time.tm_sec); 2987b302772SAlexandre Belloni 2997b302772SAlexandre Belloni FD_ZERO(&readfds); 3007b302772SAlexandre Belloni FD_SET(self->fd, &readfds); 3017b302772SAlexandre Belloni 3027b302772SAlexandre Belloni rc = select(self->fd + 1, &readfds, NULL, NULL, &tv); 3037b302772SAlexandre Belloni ASSERT_NE(-1, rc); 3047b302772SAlexandre Belloni ASSERT_NE(0, rc); 3057b302772SAlexandre Belloni 3067b302772SAlexandre Belloni rc = read(self->fd, &data, sizeof(unsigned long)); 3077b302772SAlexandre Belloni ASSERT_NE(-1, rc); 3087b302772SAlexandre Belloni 3097b302772SAlexandre Belloni rc = ioctl(self->fd, RTC_RD_TIME, &tm); 3107b302772SAlexandre Belloni ASSERT_NE(-1, rc); 3117b302772SAlexandre Belloni 3127b302772SAlexandre Belloni new = timegm((struct tm *)&tm); 3137b302772SAlexandre Belloni ASSERT_EQ(new, secs); 3147b302772SAlexandre Belloni } 3157b302772SAlexandre 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