11e8a1faeSThomas Huth /*
21e8a1faeSThomas Huth * QTest testcase for the MC146818 real-time clock
31e8a1faeSThomas Huth *
41e8a1faeSThomas Huth * Copyright IBM, Corp. 2012
51e8a1faeSThomas Huth *
61e8a1faeSThomas Huth * Authors:
71e8a1faeSThomas Huth * Anthony Liguori <aliguori@us.ibm.com>
81e8a1faeSThomas Huth *
91e8a1faeSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or later.
101e8a1faeSThomas Huth * See the COPYING file in the top-level directory.
111e8a1faeSThomas Huth *
121e8a1faeSThomas Huth */
131e8a1faeSThomas Huth
141e8a1faeSThomas Huth #include "qemu/osdep.h"
151e8a1faeSThomas Huth
161e8a1faeSThomas Huth #include "libqtest-single.h"
171e8a1faeSThomas Huth #include "qemu/timer.h"
181e8a1faeSThomas Huth #include "hw/rtc/mc146818rtc.h"
191e8a1faeSThomas Huth #include "hw/rtc/mc146818rtc_regs.h"
201e8a1faeSThomas Huth
211e8a1faeSThomas Huth #define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768)
221e8a1faeSThomas Huth
231e8a1faeSThomas Huth static uint8_t base = 0x70;
241e8a1faeSThomas Huth
bcd2dec(int value)251e8a1faeSThomas Huth static int bcd2dec(int value)
261e8a1faeSThomas Huth {
271e8a1faeSThomas Huth return (((value >> 4) & 0x0F) * 10) + (value & 0x0F);
281e8a1faeSThomas Huth }
291e8a1faeSThomas Huth
cmos_read(uint8_t reg)301e8a1faeSThomas Huth static uint8_t cmos_read(uint8_t reg)
311e8a1faeSThomas Huth {
321e8a1faeSThomas Huth outb(base + 0, reg);
331e8a1faeSThomas Huth return inb(base + 1);
341e8a1faeSThomas Huth }
351e8a1faeSThomas Huth
cmos_write(uint8_t reg,uint8_t val)361e8a1faeSThomas Huth static void cmos_write(uint8_t reg, uint8_t val)
371e8a1faeSThomas Huth {
381e8a1faeSThomas Huth outb(base + 0, reg);
391e8a1faeSThomas Huth outb(base + 1, val);
401e8a1faeSThomas Huth }
411e8a1faeSThomas Huth
tm_cmp(struct tm * lhs,struct tm * rhs)421e8a1faeSThomas Huth static int tm_cmp(struct tm *lhs, struct tm *rhs)
431e8a1faeSThomas Huth {
441e8a1faeSThomas Huth time_t a, b;
451e8a1faeSThomas Huth struct tm d1, d2;
461e8a1faeSThomas Huth
471e8a1faeSThomas Huth memcpy(&d1, lhs, sizeof(d1));
481e8a1faeSThomas Huth memcpy(&d2, rhs, sizeof(d2));
491e8a1faeSThomas Huth
501e8a1faeSThomas Huth a = mktime(&d1);
511e8a1faeSThomas Huth b = mktime(&d2);
521e8a1faeSThomas Huth
531e8a1faeSThomas Huth if (a < b) {
541e8a1faeSThomas Huth return -1;
551e8a1faeSThomas Huth } else if (a > b) {
561e8a1faeSThomas Huth return 1;
571e8a1faeSThomas Huth }
581e8a1faeSThomas Huth
591e8a1faeSThomas Huth return 0;
601e8a1faeSThomas Huth }
611e8a1faeSThomas Huth
621e8a1faeSThomas Huth #if 0
631e8a1faeSThomas Huth static void print_tm(struct tm *tm)
641e8a1faeSThomas Huth {
651e8a1faeSThomas Huth printf("%04d-%02d-%02d %02d:%02d:%02d\n",
661e8a1faeSThomas Huth tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
671e8a1faeSThomas Huth tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_gmtoff);
681e8a1faeSThomas Huth }
691e8a1faeSThomas Huth #endif
701e8a1faeSThomas Huth
cmos_get_date_time(struct tm * date)711e8a1faeSThomas Huth static void cmos_get_date_time(struct tm *date)
721e8a1faeSThomas Huth {
731e8a1faeSThomas Huth int base_year = 2000, hour_offset;
741e8a1faeSThomas Huth int sec, min, hour, mday, mon, year;
751e8a1faeSThomas Huth time_t ts;
761e8a1faeSThomas Huth struct tm dummy;
771e8a1faeSThomas Huth
781e8a1faeSThomas Huth sec = cmos_read(RTC_SECONDS);
791e8a1faeSThomas Huth min = cmos_read(RTC_MINUTES);
801e8a1faeSThomas Huth hour = cmos_read(RTC_HOURS);
811e8a1faeSThomas Huth mday = cmos_read(RTC_DAY_OF_MONTH);
821e8a1faeSThomas Huth mon = cmos_read(RTC_MONTH);
831e8a1faeSThomas Huth year = cmos_read(RTC_YEAR);
841e8a1faeSThomas Huth
851e8a1faeSThomas Huth if ((cmos_read(RTC_REG_B) & REG_B_DM) == 0) {
861e8a1faeSThomas Huth sec = bcd2dec(sec);
871e8a1faeSThomas Huth min = bcd2dec(min);
881e8a1faeSThomas Huth hour = bcd2dec(hour);
891e8a1faeSThomas Huth mday = bcd2dec(mday);
901e8a1faeSThomas Huth mon = bcd2dec(mon);
911e8a1faeSThomas Huth year = bcd2dec(year);
921e8a1faeSThomas Huth hour_offset = 80;
931e8a1faeSThomas Huth } else {
941e8a1faeSThomas Huth hour_offset = 0x80;
951e8a1faeSThomas Huth }
961e8a1faeSThomas Huth
971e8a1faeSThomas Huth if ((cmos_read(0x0B) & REG_B_24H) == 0) {
981e8a1faeSThomas Huth if (hour >= hour_offset) {
991e8a1faeSThomas Huth hour -= hour_offset;
1001e8a1faeSThomas Huth hour += 12;
1011e8a1faeSThomas Huth }
1021e8a1faeSThomas Huth }
1031e8a1faeSThomas Huth
1041e8a1faeSThomas Huth ts = time(NULL);
1051e8a1faeSThomas Huth localtime_r(&ts, &dummy);
1061e8a1faeSThomas Huth
1071e8a1faeSThomas Huth date->tm_isdst = dummy.tm_isdst;
1081e8a1faeSThomas Huth date->tm_sec = sec;
1091e8a1faeSThomas Huth date->tm_min = min;
1101e8a1faeSThomas Huth date->tm_hour = hour;
1111e8a1faeSThomas Huth date->tm_mday = mday;
1121e8a1faeSThomas Huth date->tm_mon = mon - 1;
1131e8a1faeSThomas Huth date->tm_year = base_year + year - 1900;
114*f10225d7SBin Meng #if !defined(__sun__) && !defined(_WIN32)
1151e8a1faeSThomas Huth date->tm_gmtoff = 0;
1161e8a1faeSThomas Huth #endif
1171e8a1faeSThomas Huth
1181e8a1faeSThomas Huth ts = mktime(date);
1191e8a1faeSThomas Huth }
1201e8a1faeSThomas Huth
check_time(int wiggle)1211e8a1faeSThomas Huth static void check_time(int wiggle)
1221e8a1faeSThomas Huth {
1231e8a1faeSThomas Huth struct tm start, date[4], end;
1241e8a1faeSThomas Huth struct tm *datep;
1251e8a1faeSThomas Huth time_t ts;
1261e8a1faeSThomas Huth
1271e8a1faeSThomas Huth /*
1281e8a1faeSThomas Huth * This check assumes a few things. First, we cannot guarantee that we get
1291e8a1faeSThomas Huth * a consistent reading from the wall clock because we may hit an edge of
1301e8a1faeSThomas Huth * the clock while reading. To work around this, we read four clock readings
1311e8a1faeSThomas Huth * such that at least two of them should match. We need to assume that one
1321e8a1faeSThomas Huth * reading is corrupt so we need four readings to ensure that we have at
1331e8a1faeSThomas Huth * least two consecutive identical readings
1341e8a1faeSThomas Huth *
1351e8a1faeSThomas Huth * It's also possible that we'll cross an edge reading the host clock so
1361e8a1faeSThomas Huth * simply check to make sure that the clock reading is within the period of
1371e8a1faeSThomas Huth * when we expect it to be.
1381e8a1faeSThomas Huth */
1391e8a1faeSThomas Huth
1401e8a1faeSThomas Huth ts = time(NULL);
1411e8a1faeSThomas Huth gmtime_r(&ts, &start);
1421e8a1faeSThomas Huth
1431e8a1faeSThomas Huth cmos_get_date_time(&date[0]);
1441e8a1faeSThomas Huth cmos_get_date_time(&date[1]);
1451e8a1faeSThomas Huth cmos_get_date_time(&date[2]);
1461e8a1faeSThomas Huth cmos_get_date_time(&date[3]);
1471e8a1faeSThomas Huth
1481e8a1faeSThomas Huth ts = time(NULL);
1491e8a1faeSThomas Huth gmtime_r(&ts, &end);
1501e8a1faeSThomas Huth
1511e8a1faeSThomas Huth if (tm_cmp(&date[0], &date[1]) == 0) {
1521e8a1faeSThomas Huth datep = &date[0];
1531e8a1faeSThomas Huth } else if (tm_cmp(&date[1], &date[2]) == 0) {
1541e8a1faeSThomas Huth datep = &date[1];
1551e8a1faeSThomas Huth } else if (tm_cmp(&date[2], &date[3]) == 0) {
1561e8a1faeSThomas Huth datep = &date[2];
1571e8a1faeSThomas Huth } else {
1581e8a1faeSThomas Huth g_assert_not_reached();
1591e8a1faeSThomas Huth }
1601e8a1faeSThomas Huth
1611e8a1faeSThomas Huth if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) {
1621e8a1faeSThomas Huth long t, s;
1631e8a1faeSThomas Huth
1641e8a1faeSThomas Huth start.tm_isdst = datep->tm_isdst;
1651e8a1faeSThomas Huth
1661e8a1faeSThomas Huth t = (long)mktime(datep);
1671e8a1faeSThomas Huth s = (long)mktime(&start);
1681e8a1faeSThomas Huth if (t < s) {
1691e8a1faeSThomas Huth g_test_message("RTC is %ld second(s) behind wall-clock", (s - t));
1701e8a1faeSThomas Huth } else {
1711e8a1faeSThomas Huth g_test_message("RTC is %ld second(s) ahead of wall-clock", (t - s));
1721e8a1faeSThomas Huth }
1731e8a1faeSThomas Huth
1741e8a1faeSThomas Huth g_assert_cmpint(ABS(t - s), <=, wiggle);
1751e8a1faeSThomas Huth }
1761e8a1faeSThomas Huth }
1771e8a1faeSThomas Huth
1781e8a1faeSThomas Huth static int wiggle = 2;
1791e8a1faeSThomas Huth
set_year_20xx(void)1801e8a1faeSThomas Huth static void set_year_20xx(void)
1811e8a1faeSThomas Huth {
1821e8a1faeSThomas Huth /* Set BCD mode */
1831e8a1faeSThomas Huth cmos_write(RTC_REG_B, REG_B_24H);
1841e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x76);
1851e8a1faeSThomas Huth cmos_write(RTC_YEAR, 0x11);
1861e8a1faeSThomas Huth cmos_write(RTC_CENTURY, 0x20);
1871e8a1faeSThomas Huth cmos_write(RTC_MONTH, 0x02);
1881e8a1faeSThomas Huth cmos_write(RTC_DAY_OF_MONTH, 0x02);
1891e8a1faeSThomas Huth cmos_write(RTC_HOURS, 0x02);
1901e8a1faeSThomas Huth cmos_write(RTC_MINUTES, 0x04);
1911e8a1faeSThomas Huth cmos_write(RTC_SECONDS, 0x58);
1921e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x26);
1931e8a1faeSThomas Huth
1941e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
1951e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
1961e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
1971e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
1981e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
1991e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11);
2001e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20);
2011e8a1faeSThomas Huth
2021e8a1faeSThomas Huth if (sizeof(time_t) == 4) {
2031e8a1faeSThomas Huth return;
2041e8a1faeSThomas Huth }
2051e8a1faeSThomas Huth
2061e8a1faeSThomas Huth /* Set a date in 2080 to ensure there is no year-2038 overflow. */
2071e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x76);
2081e8a1faeSThomas Huth cmos_write(RTC_YEAR, 0x80);
2091e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x26);
2101e8a1faeSThomas Huth
2111e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
2121e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
2131e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
2141e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
2151e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
2161e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x80);
2171e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20);
2181e8a1faeSThomas Huth
2191e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x76);
2201e8a1faeSThomas Huth cmos_write(RTC_YEAR, 0x11);
2211e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x26);
2221e8a1faeSThomas Huth
2231e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
2241e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
2251e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
2261e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
2271e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
2281e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11);
2291e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20);
2301e8a1faeSThomas Huth }
2311e8a1faeSThomas Huth
set_year_1980(void)2321e8a1faeSThomas Huth static void set_year_1980(void)
2331e8a1faeSThomas Huth {
2341e8a1faeSThomas Huth /* Set BCD mode */
2351e8a1faeSThomas Huth cmos_write(RTC_REG_B, REG_B_24H);
2361e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x76);
2371e8a1faeSThomas Huth cmos_write(RTC_YEAR, 0x80);
2381e8a1faeSThomas Huth cmos_write(RTC_CENTURY, 0x19);
2391e8a1faeSThomas Huth cmos_write(RTC_MONTH, 0x02);
2401e8a1faeSThomas Huth cmos_write(RTC_DAY_OF_MONTH, 0x02);
2411e8a1faeSThomas Huth cmos_write(RTC_HOURS, 0x02);
2421e8a1faeSThomas Huth cmos_write(RTC_MINUTES, 0x04);
2431e8a1faeSThomas Huth cmos_write(RTC_SECONDS, 0x58);
2441e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x26);
2451e8a1faeSThomas Huth
2461e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
2471e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
2481e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
2491e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
2501e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
2511e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x80);
2521e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x19);
2531e8a1faeSThomas Huth }
2541e8a1faeSThomas Huth
bcd_check_time(void)2551e8a1faeSThomas Huth static void bcd_check_time(void)
2561e8a1faeSThomas Huth {
2571e8a1faeSThomas Huth /* Set BCD mode */
2581e8a1faeSThomas Huth cmos_write(RTC_REG_B, REG_B_24H);
2591e8a1faeSThomas Huth check_time(wiggle);
2601e8a1faeSThomas Huth }
2611e8a1faeSThomas Huth
dec_check_time(void)2621e8a1faeSThomas Huth static void dec_check_time(void)
2631e8a1faeSThomas Huth {
2641e8a1faeSThomas Huth /* Set DEC mode */
2651e8a1faeSThomas Huth cmos_write(RTC_REG_B, REG_B_24H | REG_B_DM);
2661e8a1faeSThomas Huth check_time(wiggle);
2671e8a1faeSThomas Huth }
2681e8a1faeSThomas Huth
alarm_time(void)2691e8a1faeSThomas Huth static void alarm_time(void)
2701e8a1faeSThomas Huth {
2711e8a1faeSThomas Huth struct tm now;
2721e8a1faeSThomas Huth time_t ts;
2731e8a1faeSThomas Huth int i;
2741e8a1faeSThomas Huth
2751e8a1faeSThomas Huth ts = time(NULL);
2761e8a1faeSThomas Huth gmtime_r(&ts, &now);
2771e8a1faeSThomas Huth
2781e8a1faeSThomas Huth /* set DEC mode */
2791e8a1faeSThomas Huth cmos_write(RTC_REG_B, REG_B_24H | REG_B_DM);
2801e8a1faeSThomas Huth
2811e8a1faeSThomas Huth g_assert(!get_irq(RTC_ISA_IRQ));
2821e8a1faeSThomas Huth cmos_read(RTC_REG_C);
2831e8a1faeSThomas Huth
2841e8a1faeSThomas Huth now.tm_sec = (now.tm_sec + 2) % 60;
2851e8a1faeSThomas Huth cmos_write(RTC_SECONDS_ALARM, now.tm_sec);
2861e8a1faeSThomas Huth cmos_write(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE);
2871e8a1faeSThomas Huth cmos_write(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE);
2881e8a1faeSThomas Huth cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_AIE);
2891e8a1faeSThomas Huth
2901e8a1faeSThomas Huth for (i = 0; i < 2 + wiggle; i++) {
2911e8a1faeSThomas Huth if (get_irq(RTC_ISA_IRQ)) {
2921e8a1faeSThomas Huth break;
2931e8a1faeSThomas Huth }
2941e8a1faeSThomas Huth
2954f3ccd4fSPhilippe Mathieu-Daudé clock_step(NANOSECONDS_PER_SECOND);
2961e8a1faeSThomas Huth }
2971e8a1faeSThomas Huth
2981e8a1faeSThomas Huth g_assert(get_irq(RTC_ISA_IRQ));
2991e8a1faeSThomas Huth g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0);
3001e8a1faeSThomas Huth g_assert(cmos_read(RTC_REG_C) == 0);
3011e8a1faeSThomas Huth }
3021e8a1faeSThomas Huth
set_time_regs(int h,int m,int s)3031e8a1faeSThomas Huth static void set_time_regs(int h, int m, int s)
3041e8a1faeSThomas Huth {
3051e8a1faeSThomas Huth cmos_write(RTC_HOURS, h);
3061e8a1faeSThomas Huth cmos_write(RTC_MINUTES, m);
3071e8a1faeSThomas Huth cmos_write(RTC_SECONDS, s);
3081e8a1faeSThomas Huth }
3091e8a1faeSThomas Huth
set_time(int mode,int h,int m,int s)3101e8a1faeSThomas Huth static void set_time(int mode, int h, int m, int s)
3111e8a1faeSThomas Huth {
3121e8a1faeSThomas Huth cmos_write(RTC_REG_B, mode);
3131e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x76);
3141e8a1faeSThomas Huth set_time_regs(h, m, s);
3151e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x26);
3161e8a1faeSThomas Huth }
3171e8a1faeSThomas Huth
set_datetime_bcd(int h,int min,int s,int d,int m,int y)3181e8a1faeSThomas Huth static void set_datetime_bcd(int h, int min, int s, int d, int m, int y)
3191e8a1faeSThomas Huth {
3201e8a1faeSThomas Huth cmos_write(RTC_HOURS, h);
3211e8a1faeSThomas Huth cmos_write(RTC_MINUTES, min);
3221e8a1faeSThomas Huth cmos_write(RTC_SECONDS, s);
3231e8a1faeSThomas Huth cmos_write(RTC_YEAR, y & 0xFF);
3241e8a1faeSThomas Huth cmos_write(RTC_CENTURY, y >> 8);
3251e8a1faeSThomas Huth cmos_write(RTC_MONTH, m);
3261e8a1faeSThomas Huth cmos_write(RTC_DAY_OF_MONTH, d);
3271e8a1faeSThomas Huth }
3281e8a1faeSThomas Huth
set_datetime_dec(int h,int min,int s,int d,int m,int y)3291e8a1faeSThomas Huth static void set_datetime_dec(int h, int min, int s, int d, int m, int y)
3301e8a1faeSThomas Huth {
3311e8a1faeSThomas Huth cmos_write(RTC_HOURS, h);
3321e8a1faeSThomas Huth cmos_write(RTC_MINUTES, min);
3331e8a1faeSThomas Huth cmos_write(RTC_SECONDS, s);
3341e8a1faeSThomas Huth cmos_write(RTC_YEAR, y % 100);
3351e8a1faeSThomas Huth cmos_write(RTC_CENTURY, y / 100);
3361e8a1faeSThomas Huth cmos_write(RTC_MONTH, m);
3371e8a1faeSThomas Huth cmos_write(RTC_DAY_OF_MONTH, d);
3381e8a1faeSThomas Huth }
3391e8a1faeSThomas Huth
set_datetime(int mode,int h,int min,int s,int d,int m,int y)3401e8a1faeSThomas Huth static void set_datetime(int mode, int h, int min, int s, int d, int m, int y)
3411e8a1faeSThomas Huth {
3421e8a1faeSThomas Huth cmos_write(RTC_REG_B, mode);
3431e8a1faeSThomas Huth
3441e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x76);
3451e8a1faeSThomas Huth if (mode & REG_B_DM) {
3461e8a1faeSThomas Huth set_datetime_dec(h, min, s, d, m, y);
3471e8a1faeSThomas Huth } else {
3481e8a1faeSThomas Huth set_datetime_bcd(h, min, s, d, m, y);
3491e8a1faeSThomas Huth }
3501e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x26);
3511e8a1faeSThomas Huth }
3521e8a1faeSThomas Huth
3531e8a1faeSThomas Huth #define assert_time(h, m, s) \
3541e8a1faeSThomas Huth do { \
3551e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_HOURS), ==, h); \
3561e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_MINUTES), ==, m); \
3571e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_SECONDS), ==, s); \
3581e8a1faeSThomas Huth } while(0)
3591e8a1faeSThomas Huth
3601e8a1faeSThomas Huth #define assert_datetime_bcd(h, min, s, d, m, y) \
3611e8a1faeSThomas Huth do { \
3621e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_HOURS), ==, h); \
3631e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_MINUTES), ==, min); \
3641e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_SECONDS), ==, s); \
3651e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, d); \
3661e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_MONTH), ==, m); \
3671e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_YEAR), ==, (y & 0xFF)); \
3681e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_CENTURY), ==, (y >> 8)); \
3691e8a1faeSThomas Huth } while(0)
3701e8a1faeSThomas Huth
basic_12h_bcd(void)3711e8a1faeSThomas Huth static void basic_12h_bcd(void)
3721e8a1faeSThomas Huth {
3731e8a1faeSThomas Huth /* set BCD 12 hour mode */
3741e8a1faeSThomas Huth set_time(0, 0x81, 0x59, 0x00);
3751e8a1faeSThomas Huth clock_step(1000000000LL);
3761e8a1faeSThomas Huth assert_time(0x81, 0x59, 0x01);
3771e8a1faeSThomas Huth clock_step(59000000000LL);
3781e8a1faeSThomas Huth assert_time(0x82, 0x00, 0x00);
3791e8a1faeSThomas Huth
3801e8a1faeSThomas Huth /* test BCD wraparound */
3811e8a1faeSThomas Huth set_time(0, 0x09, 0x59, 0x59);
3821e8a1faeSThomas Huth clock_step(60000000000LL);
3831e8a1faeSThomas Huth assert_time(0x10, 0x00, 0x59);
3841e8a1faeSThomas Huth
3851e8a1faeSThomas Huth /* 12 AM -> 1 AM */
3861e8a1faeSThomas Huth set_time(0, 0x12, 0x59, 0x59);
3871e8a1faeSThomas Huth clock_step(1000000000LL);
3881e8a1faeSThomas Huth assert_time(0x01, 0x00, 0x00);
3891e8a1faeSThomas Huth
3901e8a1faeSThomas Huth /* 12 PM -> 1 PM */
3911e8a1faeSThomas Huth set_time(0, 0x92, 0x59, 0x59);
3921e8a1faeSThomas Huth clock_step(1000000000LL);
3931e8a1faeSThomas Huth assert_time(0x81, 0x00, 0x00);
3941e8a1faeSThomas Huth
3951e8a1faeSThomas Huth /* 11 AM -> 12 PM */
3961e8a1faeSThomas Huth set_time(0, 0x11, 0x59, 0x59);
3971e8a1faeSThomas Huth clock_step(1000000000LL);
3981e8a1faeSThomas Huth assert_time(0x92, 0x00, 0x00);
3991e8a1faeSThomas Huth /* TODO: test day wraparound */
4001e8a1faeSThomas Huth
4011e8a1faeSThomas Huth /* 11 PM -> 12 AM */
4021e8a1faeSThomas Huth set_time(0, 0x91, 0x59, 0x59);
4031e8a1faeSThomas Huth clock_step(1000000000LL);
4041e8a1faeSThomas Huth assert_time(0x12, 0x00, 0x00);
4051e8a1faeSThomas Huth /* TODO: test day wraparound */
4061e8a1faeSThomas Huth }
4071e8a1faeSThomas Huth
basic_12h_dec(void)4081e8a1faeSThomas Huth static void basic_12h_dec(void)
4091e8a1faeSThomas Huth {
4101e8a1faeSThomas Huth /* set decimal 12 hour mode */
4111e8a1faeSThomas Huth set_time(REG_B_DM, 0x81, 59, 0);
4121e8a1faeSThomas Huth clock_step(1000000000LL);
4131e8a1faeSThomas Huth assert_time(0x81, 59, 1);
4141e8a1faeSThomas Huth clock_step(59000000000LL);
4151e8a1faeSThomas Huth assert_time(0x82, 0, 0);
4161e8a1faeSThomas Huth
4171e8a1faeSThomas Huth /* 12 PM -> 1 PM */
4181e8a1faeSThomas Huth set_time(REG_B_DM, 0x8c, 59, 59);
4191e8a1faeSThomas Huth clock_step(1000000000LL);
4201e8a1faeSThomas Huth assert_time(0x81, 0, 0);
4211e8a1faeSThomas Huth
4221e8a1faeSThomas Huth /* 12 AM -> 1 AM */
4231e8a1faeSThomas Huth set_time(REG_B_DM, 0x0c, 59, 59);
4241e8a1faeSThomas Huth clock_step(1000000000LL);
4251e8a1faeSThomas Huth assert_time(0x01, 0, 0);
4261e8a1faeSThomas Huth
4271e8a1faeSThomas Huth /* 11 AM -> 12 PM */
4281e8a1faeSThomas Huth set_time(REG_B_DM, 0x0b, 59, 59);
4291e8a1faeSThomas Huth clock_step(1000000000LL);
4301e8a1faeSThomas Huth assert_time(0x8c, 0, 0);
4311e8a1faeSThomas Huth
4321e8a1faeSThomas Huth /* 11 PM -> 12 AM */
4331e8a1faeSThomas Huth set_time(REG_B_DM, 0x8b, 59, 59);
4341e8a1faeSThomas Huth clock_step(1000000000LL);
4351e8a1faeSThomas Huth assert_time(0x0c, 0, 0);
4361e8a1faeSThomas Huth /* TODO: test day wraparound */
4371e8a1faeSThomas Huth }
4381e8a1faeSThomas Huth
basic_24h_bcd(void)4391e8a1faeSThomas Huth static void basic_24h_bcd(void)
4401e8a1faeSThomas Huth {
4411e8a1faeSThomas Huth /* set BCD 24 hour mode */
4421e8a1faeSThomas Huth set_time(REG_B_24H, 0x09, 0x59, 0x00);
4431e8a1faeSThomas Huth clock_step(1000000000LL);
4441e8a1faeSThomas Huth assert_time(0x09, 0x59, 0x01);
4451e8a1faeSThomas Huth clock_step(59000000000LL);
4461e8a1faeSThomas Huth assert_time(0x10, 0x00, 0x00);
4471e8a1faeSThomas Huth
4481e8a1faeSThomas Huth /* test BCD wraparound */
4491e8a1faeSThomas Huth set_time(REG_B_24H, 0x09, 0x59, 0x00);
4501e8a1faeSThomas Huth clock_step(60000000000LL);
4511e8a1faeSThomas Huth assert_time(0x10, 0x00, 0x00);
4521e8a1faeSThomas Huth
4531e8a1faeSThomas Huth /* TODO: test day wraparound */
4541e8a1faeSThomas Huth set_time(REG_B_24H, 0x23, 0x59, 0x00);
4551e8a1faeSThomas Huth clock_step(60000000000LL);
4561e8a1faeSThomas Huth assert_time(0x00, 0x00, 0x00);
4571e8a1faeSThomas Huth }
4581e8a1faeSThomas Huth
basic_24h_dec(void)4591e8a1faeSThomas Huth static void basic_24h_dec(void)
4601e8a1faeSThomas Huth {
4611e8a1faeSThomas Huth /* set decimal 24 hour mode */
4621e8a1faeSThomas Huth set_time(REG_B_24H | REG_B_DM, 9, 59, 0);
4631e8a1faeSThomas Huth clock_step(1000000000LL);
4641e8a1faeSThomas Huth assert_time(9, 59, 1);
4651e8a1faeSThomas Huth clock_step(59000000000LL);
4661e8a1faeSThomas Huth assert_time(10, 0, 0);
4671e8a1faeSThomas Huth
4681e8a1faeSThomas Huth /* test BCD wraparound */
4691e8a1faeSThomas Huth set_time(REG_B_24H | REG_B_DM, 9, 59, 0);
4701e8a1faeSThomas Huth clock_step(60000000000LL);
4711e8a1faeSThomas Huth assert_time(10, 0, 0);
4721e8a1faeSThomas Huth
4731e8a1faeSThomas Huth /* TODO: test day wraparound */
4741e8a1faeSThomas Huth set_time(REG_B_24H | REG_B_DM, 23, 59, 0);
4751e8a1faeSThomas Huth clock_step(60000000000LL);
4761e8a1faeSThomas Huth assert_time(0, 0, 0);
4771e8a1faeSThomas Huth }
4781e8a1faeSThomas Huth
am_pm_alarm(void)4791e8a1faeSThomas Huth static void am_pm_alarm(void)
4801e8a1faeSThomas Huth {
4811e8a1faeSThomas Huth cmos_write(RTC_MINUTES_ALARM, 0xC0);
4821e8a1faeSThomas Huth cmos_write(RTC_SECONDS_ALARM, 0xC0);
4831e8a1faeSThomas Huth
4841e8a1faeSThomas Huth /* set BCD 12 hour mode */
4851e8a1faeSThomas Huth cmos_write(RTC_REG_B, 0);
4861e8a1faeSThomas Huth
4871e8a1faeSThomas Huth /* Set time and alarm hour. */
4881e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x76);
4891e8a1faeSThomas Huth cmos_write(RTC_HOURS_ALARM, 0x82);
4901e8a1faeSThomas Huth cmos_write(RTC_HOURS, 0x81);
4911e8a1faeSThomas Huth cmos_write(RTC_MINUTES, 0x59);
4921e8a1faeSThomas Huth cmos_write(RTC_SECONDS, 0x00);
4931e8a1faeSThomas Huth cmos_read(RTC_REG_C);
4941e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x26);
4951e8a1faeSThomas Huth
4961e8a1faeSThomas Huth /* Check that alarm triggers when AM/PM is set. */
4971e8a1faeSThomas Huth clock_step(60000000000LL);
4981e8a1faeSThomas Huth g_assert(cmos_read(RTC_HOURS) == 0x82);
4991e8a1faeSThomas Huth g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0);
5001e8a1faeSThomas Huth
5011e8a1faeSThomas Huth /*
5021e8a1faeSThomas Huth * Each of the following two tests takes over 60 seconds due to the time
5031e8a1faeSThomas Huth * needed to report the PIT interrupts. Unfortunately, our PIT device
5041e8a1faeSThomas Huth * model keeps counting even when GATE=0, so we cannot simply disable
5051e8a1faeSThomas Huth * it in main().
5061e8a1faeSThomas Huth */
5071e8a1faeSThomas Huth if (g_test_quick()) {
5081e8a1faeSThomas Huth return;
5091e8a1faeSThomas Huth }
5101e8a1faeSThomas Huth
5111e8a1faeSThomas Huth /* set DEC 12 hour mode */
5121e8a1faeSThomas Huth cmos_write(RTC_REG_B, REG_B_DM);
5131e8a1faeSThomas Huth
5141e8a1faeSThomas Huth /* Set time and alarm hour. */
5151e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x76);
5161e8a1faeSThomas Huth cmos_write(RTC_HOURS_ALARM, 0x82);
5171e8a1faeSThomas Huth cmos_write(RTC_HOURS, 3);
5181e8a1faeSThomas Huth cmos_write(RTC_MINUTES, 0);
5191e8a1faeSThomas Huth cmos_write(RTC_SECONDS, 0);
5201e8a1faeSThomas Huth cmos_read(RTC_REG_C);
5211e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x26);
5221e8a1faeSThomas Huth
5231e8a1faeSThomas Huth /* Check that alarm triggers. */
5241e8a1faeSThomas Huth clock_step(3600 * 11 * 1000000000LL);
5251e8a1faeSThomas Huth g_assert(cmos_read(RTC_HOURS) == 0x82);
5261e8a1faeSThomas Huth g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0);
5271e8a1faeSThomas Huth
5281e8a1faeSThomas Huth /* Same as above, with inverted HOURS and HOURS_ALARM. */
5291e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x76);
5301e8a1faeSThomas Huth cmos_write(RTC_HOURS_ALARM, 2);
5311e8a1faeSThomas Huth cmos_write(RTC_HOURS, 3);
5321e8a1faeSThomas Huth cmos_write(RTC_MINUTES, 0);
5331e8a1faeSThomas Huth cmos_write(RTC_SECONDS, 0);
5341e8a1faeSThomas Huth cmos_read(RTC_REG_C);
5351e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x26);
5361e8a1faeSThomas Huth
5371e8a1faeSThomas Huth /* Check that alarm does not trigger if hours differ only by AM/PM. */
5381e8a1faeSThomas Huth clock_step(3600 * 11 * 1000000000LL);
5391e8a1faeSThomas Huth g_assert(cmos_read(RTC_HOURS) == 0x82);
5401e8a1faeSThomas Huth g_assert((cmos_read(RTC_REG_C) & REG_C_AF) == 0);
5411e8a1faeSThomas Huth }
5421e8a1faeSThomas Huth
5431e8a1faeSThomas Huth /* success if no crash or abort */
fuzz_registers(void)5441e8a1faeSThomas Huth static void fuzz_registers(void)
5451e8a1faeSThomas Huth {
5461e8a1faeSThomas Huth unsigned int i;
5471e8a1faeSThomas Huth
5481e8a1faeSThomas Huth for (i = 0; i < 1000; i++) {
5491e8a1faeSThomas Huth uint8_t reg, val;
5501e8a1faeSThomas Huth
5511e8a1faeSThomas Huth reg = (uint8_t)g_test_rand_int_range(0, 16);
5521e8a1faeSThomas Huth val = (uint8_t)g_test_rand_int_range(0, 256);
5531e8a1faeSThomas Huth
5541e8a1faeSThomas Huth cmos_write(reg, val);
5551e8a1faeSThomas Huth cmos_read(reg);
5561e8a1faeSThomas Huth }
5571e8a1faeSThomas Huth }
5581e8a1faeSThomas Huth
register_b_set_flag(void)5591e8a1faeSThomas Huth static void register_b_set_flag(void)
5601e8a1faeSThomas Huth {
5611e8a1faeSThomas Huth if (cmos_read(RTC_REG_A) & REG_A_UIP) {
5621e8a1faeSThomas Huth clock_step(UIP_HOLD_LENGTH + NANOSECONDS_PER_SECOND / 5);
5631e8a1faeSThomas Huth }
5641e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0);
5651e8a1faeSThomas Huth
5661e8a1faeSThomas Huth /* Enable binary-coded decimal (BCD) mode and SET flag in Register B*/
5671e8a1faeSThomas Huth cmos_write(RTC_REG_B, REG_B_24H | REG_B_SET);
5681e8a1faeSThomas Huth
5691e8a1faeSThomas Huth set_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011);
5701e8a1faeSThomas Huth
5711e8a1faeSThomas Huth assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011);
5721e8a1faeSThomas Huth
5731e8a1faeSThomas Huth /* Since SET flag is still enabled, time does not advance. */
5741e8a1faeSThomas Huth clock_step(1000000000LL);
5751e8a1faeSThomas Huth assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011);
5761e8a1faeSThomas Huth
5771e8a1faeSThomas Huth /* Disable SET flag in Register B */
5781e8a1faeSThomas Huth cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_SET);
5791e8a1faeSThomas Huth
5801e8a1faeSThomas Huth assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011);
5811e8a1faeSThomas Huth
5821e8a1faeSThomas Huth /* Since SET flag is disabled, the clock now advances. */
5831e8a1faeSThomas Huth clock_step(1000000000LL);
5841e8a1faeSThomas Huth assert_datetime_bcd(0x02, 0x04, 0x59, 0x02, 0x02, 0x2011);
5851e8a1faeSThomas Huth }
5861e8a1faeSThomas Huth
divider_reset(void)5871e8a1faeSThomas Huth static void divider_reset(void)
5881e8a1faeSThomas Huth {
5891e8a1faeSThomas Huth /* Enable binary-coded decimal (BCD) mode in Register B*/
5901e8a1faeSThomas Huth cmos_write(RTC_REG_B, REG_B_24H);
5911e8a1faeSThomas Huth
5921e8a1faeSThomas Huth /* Enter divider reset */
5931e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x76);
5941e8a1faeSThomas Huth set_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011);
5951e8a1faeSThomas Huth
5961e8a1faeSThomas Huth assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011);
5971e8a1faeSThomas Huth
5981e8a1faeSThomas Huth /* Since divider reset flag is still enabled, these are equality checks. */
5991e8a1faeSThomas Huth clock_step(1000000000LL);
6001e8a1faeSThomas Huth assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011);
6011e8a1faeSThomas Huth
6021e8a1faeSThomas Huth /* The first update ends 500 ms after divider reset */
6031e8a1faeSThomas Huth cmos_write(RTC_REG_A, 0x26);
6041e8a1faeSThomas Huth clock_step(500000000LL - UIP_HOLD_LENGTH - 1);
6051e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0);
6061e8a1faeSThomas Huth assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011);
6071e8a1faeSThomas Huth
6081e8a1faeSThomas Huth clock_step(1);
6091e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, !=, 0);
6101e8a1faeSThomas Huth clock_step(UIP_HOLD_LENGTH);
6111e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0);
6121e8a1faeSThomas Huth
6131e8a1faeSThomas Huth assert_datetime_bcd(0x02, 0x04, 0x59, 0x02, 0x02, 0x2011);
6141e8a1faeSThomas Huth }
6151e8a1faeSThomas Huth
uip_stuck(void)6161e8a1faeSThomas Huth static void uip_stuck(void)
6171e8a1faeSThomas Huth {
6181e8a1faeSThomas Huth set_datetime(REG_B_24H, 0x02, 0x04, 0x58, 0x02, 0x02, 0x2011);
6191e8a1faeSThomas Huth
6201e8a1faeSThomas Huth /* The first update ends 500 ms after divider reset */
6211e8a1faeSThomas Huth (void)cmos_read(RTC_REG_C);
6221e8a1faeSThomas Huth clock_step(500000000LL);
6231e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0);
6241e8a1faeSThomas Huth assert_datetime_bcd(0x02, 0x04, 0x59, 0x02, 0x02, 0x2011);
6251e8a1faeSThomas Huth
6261e8a1faeSThomas Huth /* UF is now set. */
6271e8a1faeSThomas Huth cmos_write(RTC_HOURS_ALARM, 0x02);
6281e8a1faeSThomas Huth cmos_write(RTC_MINUTES_ALARM, 0xC0);
6291e8a1faeSThomas Huth cmos_write(RTC_SECONDS_ALARM, 0xC0);
6301e8a1faeSThomas Huth
6311e8a1faeSThomas Huth /* Because the alarm will fire soon, reading register A will latch UIP. */
6321e8a1faeSThomas Huth clock_step(1000000000LL - UIP_HOLD_LENGTH / 2);
6331e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, !=, 0);
6341e8a1faeSThomas Huth
6351e8a1faeSThomas Huth /* Move the alarm far away. This must not cause UIP to remain stuck! */
6361e8a1faeSThomas Huth cmos_write(RTC_HOURS_ALARM, 0x03);
6371e8a1faeSThomas Huth clock_step(UIP_HOLD_LENGTH);
6381e8a1faeSThomas Huth g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0);
6391e8a1faeSThomas Huth }
6401e8a1faeSThomas Huth
6411e8a1faeSThomas Huth #define RTC_PERIOD_CODE1 13 /* 8 Hz */
6421e8a1faeSThomas Huth #define RTC_PERIOD_CODE2 15 /* 2 Hz */
6431e8a1faeSThomas Huth
6441e8a1faeSThomas Huth #define RTC_PERIOD_TEST_NR 50
6451e8a1faeSThomas Huth
wait_periodic_interrupt(uint64_t real_time)6461e8a1faeSThomas Huth static uint64_t wait_periodic_interrupt(uint64_t real_time)
6471e8a1faeSThomas Huth {
6481e8a1faeSThomas Huth while (!get_irq(RTC_ISA_IRQ)) {
6491e8a1faeSThomas Huth real_time = clock_step_next();
6501e8a1faeSThomas Huth }
6511e8a1faeSThomas Huth
6521e8a1faeSThomas Huth g_assert((cmos_read(RTC_REG_C) & REG_C_PF) != 0);
6531e8a1faeSThomas Huth return real_time;
6541e8a1faeSThomas Huth }
6551e8a1faeSThomas Huth
periodic_timer(void)6561e8a1faeSThomas Huth static void periodic_timer(void)
6571e8a1faeSThomas Huth {
6581e8a1faeSThomas Huth int i;
6591e8a1faeSThomas Huth uint64_t period_clocks, period_time, start_time, real_time;
6601e8a1faeSThomas Huth
6611e8a1faeSThomas Huth /* disable all interrupts. */
6621e8a1faeSThomas Huth cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) &
6631e8a1faeSThomas Huth ~(REG_B_PIE | REG_B_AIE | REG_B_UIE));
6641e8a1faeSThomas Huth cmos_write(RTC_REG_A, RTC_PERIOD_CODE1);
6651e8a1faeSThomas Huth /* enable periodic interrupt after properly configure the period. */
6661e8a1faeSThomas Huth cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_PIE);
6671e8a1faeSThomas Huth
6681e8a1faeSThomas Huth start_time = real_time = clock_step_next();
6691e8a1faeSThomas Huth
6701e8a1faeSThomas Huth for (i = 0; i < RTC_PERIOD_TEST_NR; i++) {
6711e8a1faeSThomas Huth cmos_write(RTC_REG_A, RTC_PERIOD_CODE1);
6721e8a1faeSThomas Huth real_time = wait_periodic_interrupt(real_time);
6731e8a1faeSThomas Huth cmos_write(RTC_REG_A, RTC_PERIOD_CODE2);
6741e8a1faeSThomas Huth real_time = wait_periodic_interrupt(real_time);
6751e8a1faeSThomas Huth }
6761e8a1faeSThomas Huth
6771e8a1faeSThomas Huth period_clocks = periodic_period_to_clock(RTC_PERIOD_CODE1) +
6781e8a1faeSThomas Huth periodic_period_to_clock(RTC_PERIOD_CODE2);
6791e8a1faeSThomas Huth period_clocks *= RTC_PERIOD_TEST_NR;
6801e8a1faeSThomas Huth period_time = periodic_clock_to_ns(period_clocks);
6811e8a1faeSThomas Huth
6821e8a1faeSThomas Huth real_time -= start_time;
6831e8a1faeSThomas Huth g_assert_cmpint(ABS((int64_t)(real_time - period_time)), <=,
6841e8a1faeSThomas Huth NANOSECONDS_PER_SECOND * 0.5);
6851e8a1faeSThomas Huth }
6861e8a1faeSThomas Huth
main(int argc,char ** argv)6871e8a1faeSThomas Huth int main(int argc, char **argv)
6881e8a1faeSThomas Huth {
6896c054176SPeter Maydell QTestState *s;
6901e8a1faeSThomas Huth int ret;
6911e8a1faeSThomas Huth
6921e8a1faeSThomas Huth g_test_init(&argc, &argv, NULL);
6931e8a1faeSThomas Huth
6941e8a1faeSThomas Huth s = qtest_start("-rtc clock=vm");
6951e8a1faeSThomas Huth qtest_irq_intercept_in(s, "ioapic");
6961e8a1faeSThomas Huth
6971e8a1faeSThomas Huth qtest_add_func("/rtc/check-time/bcd", bcd_check_time);
6981e8a1faeSThomas Huth qtest_add_func("/rtc/check-time/dec", dec_check_time);
6991e8a1faeSThomas Huth qtest_add_func("/rtc/alarm/interrupt", alarm_time);
7001e8a1faeSThomas Huth qtest_add_func("/rtc/alarm/am-pm", am_pm_alarm);
7011e8a1faeSThomas Huth qtest_add_func("/rtc/basic/dec-24h", basic_24h_dec);
7021e8a1faeSThomas Huth qtest_add_func("/rtc/basic/bcd-24h", basic_24h_bcd);
7031e8a1faeSThomas Huth qtest_add_func("/rtc/basic/dec-12h", basic_12h_dec);
7041e8a1faeSThomas Huth qtest_add_func("/rtc/basic/bcd-12h", basic_12h_bcd);
7051e8a1faeSThomas Huth qtest_add_func("/rtc/set-year/20xx", set_year_20xx);
7061e8a1faeSThomas Huth qtest_add_func("/rtc/set-year/1980", set_year_1980);
7071e8a1faeSThomas Huth qtest_add_func("/rtc/update/register_b_set_flag", register_b_set_flag);
7081e8a1faeSThomas Huth qtest_add_func("/rtc/update/divider-reset", divider_reset);
7091e8a1faeSThomas Huth qtest_add_func("/rtc/update/uip-stuck", uip_stuck);
7101e8a1faeSThomas Huth qtest_add_func("/rtc/misc/fuzz-registers", fuzz_registers);
7111e8a1faeSThomas Huth qtest_add_func("/rtc/periodic/interrupt", periodic_timer);
7121e8a1faeSThomas Huth
7131e8a1faeSThomas Huth ret = g_test_run();
7141e8a1faeSThomas Huth
7151e8a1faeSThomas Huth qtest_quit(s);
7161e8a1faeSThomas Huth
7171e8a1faeSThomas Huth return ret;
7181e8a1faeSThomas Huth }
719