mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-22 19:35:00 +07:00
d8da8665e8
Rework rtctest to use the test harness to better handle skipping tests (e.g. when alarms are not available). Also, it now handles timeout so it will not block expecting an alarm that never comes. Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Shuah Khan (Samsung OSG) <shuah@kernel.org>
239 lines
4.8 KiB
C
239 lines
4.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Real Time Clock Driver Test Program
|
|
*
|
|
* Copyright (c) 2018 Alexandre Belloni <alexandre.belloni@bootlin.com>
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <linux/rtc.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "../kselftest_harness.h"
|
|
|
|
#define NUM_UIE 3
|
|
#define ALARM_DELTA 3
|
|
|
|
static char *rtc_file = "/dev/rtc0";
|
|
|
|
FIXTURE(rtc) {
|
|
int fd;
|
|
};
|
|
|
|
FIXTURE_SETUP(rtc) {
|
|
self->fd = open(rtc_file, O_RDONLY);
|
|
ASSERT_NE(-1, self->fd);
|
|
}
|
|
|
|
FIXTURE_TEARDOWN(rtc) {
|
|
close(self->fd);
|
|
}
|
|
|
|
TEST_F(rtc, date_read) {
|
|
int rc;
|
|
struct rtc_time rtc_tm;
|
|
|
|
/* Read the RTC time/date */
|
|
rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
|
|
ASSERT_NE(-1, rc);
|
|
|
|
TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
|
|
rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
|
|
rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
|
|
}
|
|
|
|
TEST_F(rtc, uie_read) {
|
|
int i, rc, irq = 0;
|
|
unsigned long data;
|
|
|
|
/* Turn on update interrupts */
|
|
rc = ioctl(self->fd, RTC_UIE_ON, 0);
|
|
if (rc == -1) {
|
|
ASSERT_EQ(EINVAL, errno);
|
|
TH_LOG("skip update IRQs not supported.");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < NUM_UIE; i++) {
|
|
/* This read will block */
|
|
rc = read(self->fd, &data, sizeof(data));
|
|
ASSERT_NE(-1, rc);
|
|
irq++;
|
|
}
|
|
|
|
EXPECT_EQ(NUM_UIE, irq);
|
|
|
|
rc = ioctl(self->fd, RTC_UIE_OFF, 0);
|
|
ASSERT_NE(-1, rc);
|
|
}
|
|
|
|
TEST_F(rtc, uie_select) {
|
|
int i, rc, irq = 0;
|
|
unsigned long data;
|
|
|
|
/* Turn on update interrupts */
|
|
rc = ioctl(self->fd, RTC_UIE_ON, 0);
|
|
if (rc == -1) {
|
|
ASSERT_EQ(EINVAL, errno);
|
|
TH_LOG("skip update IRQs not supported.");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < NUM_UIE; i++) {
|
|
struct timeval tv = { .tv_sec = 2 };
|
|
fd_set readfds;
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_SET(self->fd, &readfds);
|
|
/* The select will wait until an RTC interrupt happens. */
|
|
rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
|
|
ASSERT_NE(-1, rc);
|
|
ASSERT_NE(0, rc);
|
|
|
|
/* This read won't block */
|
|
rc = read(self->fd, &data, sizeof(unsigned long));
|
|
ASSERT_NE(-1, rc);
|
|
irq++;
|
|
}
|
|
|
|
EXPECT_EQ(NUM_UIE, irq);
|
|
|
|
rc = ioctl(self->fd, RTC_UIE_OFF, 0);
|
|
ASSERT_NE(-1, rc);
|
|
}
|
|
|
|
TEST_F(rtc, alarm_alm_set) {
|
|
struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
|
|
unsigned long data;
|
|
struct rtc_time tm;
|
|
fd_set readfds;
|
|
time_t secs, new;
|
|
int rc;
|
|
|
|
rc = ioctl(self->fd, RTC_RD_TIME, &tm);
|
|
ASSERT_NE(-1, rc);
|
|
|
|
secs = timegm((struct tm *)&tm) + ALARM_DELTA;
|
|
gmtime_r(&secs, (struct tm *)&tm);
|
|
|
|
rc = ioctl(self->fd, RTC_ALM_SET, &tm);
|
|
if (rc == -1) {
|
|
ASSERT_EQ(EINVAL, errno);
|
|
TH_LOG("skip alarms are not supported.");
|
|
return;
|
|
}
|
|
|
|
rc = ioctl(self->fd, RTC_ALM_READ, &tm);
|
|
ASSERT_NE(-1, rc);
|
|
|
|
TH_LOG("Alarm time now set to %02d:%02d:%02d.",
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
|
|
/* Enable alarm interrupts */
|
|
rc = ioctl(self->fd, RTC_AIE_ON, 0);
|
|
ASSERT_NE(-1, rc);
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_SET(self->fd, &readfds);
|
|
|
|
rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
|
|
ASSERT_NE(-1, rc);
|
|
EXPECT_NE(0, rc);
|
|
|
|
/* Disable alarm interrupts */
|
|
rc = ioctl(self->fd, RTC_AIE_OFF, 0);
|
|
ASSERT_NE(-1, rc);
|
|
|
|
if (rc == 0)
|
|
return;
|
|
|
|
rc = read(self->fd, &data, sizeof(unsigned long));
|
|
ASSERT_NE(-1, rc);
|
|
TH_LOG("data: %lx", data);
|
|
|
|
rc = ioctl(self->fd, RTC_RD_TIME, &tm);
|
|
ASSERT_NE(-1, rc);
|
|
|
|
new = timegm((struct tm *)&tm);
|
|
ASSERT_EQ(new, secs);
|
|
}
|
|
|
|
TEST_F(rtc, alarm_wkalm_set) {
|
|
struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
|
|
struct rtc_wkalrm alarm = { 0 };
|
|
struct rtc_time tm;
|
|
unsigned long data;
|
|
fd_set readfds;
|
|
time_t secs, new;
|
|
int rc;
|
|
|
|
rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
|
|
ASSERT_NE(-1, rc);
|
|
|
|
secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
|
|
gmtime_r(&secs, (struct tm *)&alarm.time);
|
|
|
|
alarm.enabled = 1;
|
|
|
|
rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
|
|
if (rc == -1) {
|
|
ASSERT_EQ(EINVAL, errno);
|
|
TH_LOG("skip alarms are not supported.");
|
|
return;
|
|
}
|
|
|
|
rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
|
|
ASSERT_NE(-1, rc);
|
|
|
|
TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
|
|
alarm.time.tm_mday, alarm.time.tm_mon + 1,
|
|
alarm.time.tm_year + 1900, alarm.time.tm_hour,
|
|
alarm.time.tm_min, alarm.time.tm_sec);
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_SET(self->fd, &readfds);
|
|
|
|
rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
|
|
ASSERT_NE(-1, rc);
|
|
EXPECT_NE(0, rc);
|
|
|
|
rc = read(self->fd, &data, sizeof(unsigned long));
|
|
ASSERT_NE(-1, rc);
|
|
|
|
rc = ioctl(self->fd, RTC_RD_TIME, &tm);
|
|
ASSERT_NE(-1, rc);
|
|
|
|
new = timegm((struct tm *)&tm);
|
|
ASSERT_EQ(new, secs);
|
|
}
|
|
|
|
static void __attribute__((constructor))
|
|
__constructor_order_last(void)
|
|
{
|
|
if (!__constructor_order)
|
|
__constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
switch (argc) {
|
|
case 2:
|
|
rtc_file = argv[1];
|
|
/* FALLTHROUGH */
|
|
case 1:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
return test_harness_run(argc, argv);
|
|
}
|