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