xref: /openbmc/qemu/tests/qtest/rtc-test.c (revision e93ded1bf6c94ab95015b33e188bc8b0b0c32670)
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