qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Kuo-Jung Su <dantesu@gmail.com>
To: qemu-devel@nongnu.org
Cc: Peter Maydell <peter.maydell@linaro.org>,
	i.mitsyanko@samsung.com, Wei-Ren Chen <chenwj@iis.sinica.edu.tw>,
	Blue Swirl <blauwirbel@gmail.com>,
	Paul Brook <paul@codesourcery.com>,
	Kuo-Jung Su <dantesu@faraday-tech.com>,
	Paolo Bonzini <pbonzini@redhat.com>, Andreas <afaerber@suse.de>,
	fred.konrad@greensocs.com
Subject: [Qemu-devel] [PATCH v9 09/24] tests: add QTest for FTRTC011
Date: Mon, 25 Mar 2013 20:09:45 +0800	[thread overview]
Message-ID: <1364213400-10266-10-git-send-email-dantesu@gmail.com> (raw)
In-Reply-To: <1364213400-10266-1-git-send-email-dantesu@gmail.com>

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTRTC011 QEMU model is implemented without calender functions.
It acts in counter mode only, all the time & timezone conversion
relies on the c runtime library. (i.e. mktime(), localtime() ...etc)

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 tests/Makefile        |    3 +
 tests/ftrtc011-test.c |  410 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 413 insertions(+)
 create mode 100644 tests/ftrtc011-test.c

diff --git a/tests/Makefile b/tests/Makefile
index 567e36e..bc75f2b 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -75,6 +75,8 @@ gcov-files-sparc-y += hw/m48t59.c
 gcov-files-sparc64-y += hw/m48t59.c
 check-qtest-arm-y = tests/tmp105-test$(EXESUF)
 gcov-files-arm-y += hw/tmp105.c
+check-qtest-arm-y = tests/ftrtc011-test$(EXESUF)
+gcov-files-arm-y += hw/ftrtc011.c
 
 GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
 
@@ -133,6 +135,7 @@ tests/m48t59-test$(EXESUF): tests/m48t59-test.o
 tests/fdc-test$(EXESUF): tests/fdc-test.o
 tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
 tests/tmp105-test$(EXESUF): tests/tmp105-test.o
+tests/ftrtc011-test$(EXESUF): tests/ftrtc011-test.o
 
 # QTest rules
 
diff --git a/tests/ftrtc011-test.c b/tests/ftrtc011-test.c
new file mode 100644
index 0000000..15b4f0d
--- /dev/null
+++ b/tests/ftrtc011-test.c
@@ -0,0 +1,410 @@
+/*
+ * QTest testcase for the FTRTC011 real-time clock
+ *
+ * Copyright (c) 2013 Kuo-Jung Su
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "libqtest.h"
+#include "hw/ftrtc011.h"
+
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define A369_FTRTC011_BASE      0x92100000
+#define A369_FTRTC011_IRQ_ALARM 42  /* edge triggered */
+#define A369_FTRTC011_IRQ_SEC   43  /* edge triggered */
+#define A369_FTRTC011_IRQ_MIN   44  /* edge triggered */
+#define A369_FTRTC011_IRQ_HOUR  45  /* edge triggered */
+
+#define CFG_BASEYEAR            2010
+
+static time_t ts_base;
+
+static uint32_t rtc_read(uint32_t reg)
+{
+    return readl(A369_FTRTC011_BASE + reg);
+}
+
+static void rtc_write(uint32_t reg, uint32_t val)
+{
+    writel(A369_FTRTC011_BASE + reg, val);
+}
+
+static int rtc_get_irq(int irq)
+{
+#if 0   /* It looks to me that get_irq() doesn't work well
+         * with edge interrupts.
+         */
+    return get_irq(irq);
+#else
+    switch (irq) {
+    case A369_FTRTC011_IRQ_ALARM:
+        return !!(rtc_read(REG_ISR) & ISR_ALARM);
+    case A369_FTRTC011_IRQ_SEC:
+        return !!(rtc_read(REG_ISR) & ISR_SEC);
+    case A369_FTRTC011_IRQ_MIN:
+        return !!(rtc_read(REG_ISR) & ISR_MIN);
+    case A369_FTRTC011_IRQ_HOUR:
+        return !!(rtc_read(REG_ISR) & ISR_HOUR);
+    default:
+        return 0;
+    }
+#endif
+}
+
+static int tm_cmp(struct tm *lhs, struct tm *rhs)
+{
+    time_t a, b;
+    struct tm d1, d2;
+
+    memcpy(&d1, lhs, sizeof(d1));
+    memcpy(&d2, rhs, sizeof(d2));
+
+    a = mktime(&d1);
+    b = mktime(&d2);
+
+    if (a < b) {
+        return -1;
+    } else if (a > b) {
+        return 1;
+    }
+
+    return 0;
+}
+
+static void rtc_start(void)
+{
+    time_t ts;
+    struct tm base;
+
+    if (!ts_base) {
+        base.tm_isdst = -1;
+        base.tm_year  = CFG_BASEYEAR - 1900;
+        base.tm_mon   = 0;
+        base.tm_mday  = 1;
+        base.tm_hour  = 0;
+        base.tm_min   = 0;
+        base.tm_sec   = 0;
+        ts_base = mktime(&base);
+    }
+
+    ts = time(NULL) - ts_base;
+    rtc_write(REG_WDAY, ts / 86400LL);
+    ts %= 86400LL;
+    rtc_write(REG_WHOUR, ts / 3600LL);
+    ts %= 3600LL;
+    rtc_write(REG_WMIN, ts / 60LL);
+    ts %= 60LL;
+    rtc_write(REG_WSEC, ts);
+
+    rtc_write(REG_ISR, ISR_MASK);
+    rtc_write(REG_CR, CR_EN | CR_LOAD | CR_INTR_MASK);
+}
+
+static void rtc_get_datetime(struct tm *date)
+{
+    time_t ts;
+    int64_t sec, min, hour, day;
+
+    if (!ts_base) {
+        fprintf(stderr, "ts_base is not yet initialized!\n");
+        exit(1);
+    }
+
+    sec  = rtc_read(REG_SEC);
+    min  = rtc_read(REG_MIN);
+    hour = rtc_read(REG_HOUR);
+    day  = rtc_read(REG_DAY);
+    ts   = ts_base + (86400LL * day) + (hour * 3600LL) + (min * 60LL) + sec;
+
+    localtime_r(&ts, date);
+}
+
+static void rtc_test_check_time(int wiggle)
+{
+    struct tm start, date[4], end;
+    struct tm *datep;
+    time_t ts;
+
+    rtc_start();
+
+    /*
+     * This check assumes a few things.
+     * First, we cannot guarantee that we get a consistent reading
+     * from the wall clock because we may hit an edge of the clock
+     * while reading.
+     * To work around this, we read four clock readings such that
+     * at least two of them should match.  We need to assume that one
+     * reading is corrupt so we need four readings to ensure that we have at
+     * least two consecutive identical readings
+     *
+     * It's also possible that we'll cross an edge reading the host clock so
+     * simply check to make sure that the clock reading is within the period of
+     * when we expect it to be.
+     */
+
+    ts = time(NULL);
+    localtime_r(&ts, &start);
+
+    rtc_get_datetime(&date[0]);
+    rtc_get_datetime(&date[1]);
+    rtc_get_datetime(&date[2]);
+    rtc_get_datetime(&date[3]);
+
+    ts = time(NULL);
+    localtime_r(&ts, &end);
+
+    if (tm_cmp(&date[0], &date[1]) == 0) {
+        datep = &date[0];
+    } else if (tm_cmp(&date[1], &date[2]) == 0) {
+        datep = &date[1];
+    } else if (tm_cmp(&date[2], &date[3]) == 0) {
+        datep = &date[2];
+    } else {
+        g_assert_not_reached();
+    }
+
+    if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) {
+        time_t t, s;
+
+        start.tm_isdst = datep->tm_isdst;
+
+        t = mktime(datep);
+        s = mktime(&start);
+        if (t < s) {
+            g_test_message("RTC is %ld second(s) behind wall-clock\n",
+                           (s - t));
+        } else {
+            g_test_message("RTC is %ld second(s) ahead of wall-clock\n",
+                           (t - s));
+        }
+
+        g_assert_cmpint(ABS(t - s), <=, wiggle);
+    }
+}
+
+static int wiggle = 2;
+
+static void rtc_test_intr_alarm(void)
+{
+    time_t ts;
+    int i;
+
+    rtc_start();
+
+    g_assert(!rtc_get_irq(A369_FTRTC011_IRQ_ALARM));
+
+    /* schedule an alarm at 2 sec later */
+    ts = time(NULL) + 2 - ts_base;
+    ts %= 86400LL;
+    rtc_write(REG_ALARM_HOUR, ts / 3600LL);
+    ts %= 3600LL;
+    rtc_write(REG_ALARM_MIN,  ts / 60LL);
+    ts %= 60LL;
+    rtc_write(REG_ALARM_SEC,  ts);
+
+    for (i = 0; i < 2 + wiggle; i++) {
+        /*
+         * Since it's an edge interrupt (pulse),
+         * is it always possible to get the high level signal observed?
+         */
+        if (rtc_get_irq(A369_FTRTC011_IRQ_ALARM)) {
+            break;
+        }
+
+        clock_step(1000000000LL);
+    }
+
+    g_assert(i < 2 + wiggle);
+}
+
+static void rtc_test_intr_sec(void)
+{
+    int i;
+
+    rtc_start();
+
+    g_assert(!rtc_get_irq(A369_FTRTC011_IRQ_SEC));
+
+    for (i = 2; i > 0; i--) {
+
+        if (rtc_get_irq(A369_FTRTC011_IRQ_SEC)) {
+            break;
+        }
+
+        clock_step(1000000000);
+    }
+
+    g_assert(i > 0);
+}
+
+static void rtc_test_intr_min(void)
+{
+    int i;
+
+    rtc_start();
+
+    g_assert(!get_irq(A369_FTRTC011_IRQ_MIN));
+
+    for (i = 60 + 2; i > 0; i--) {
+
+        if (rtc_get_irq(A369_FTRTC011_IRQ_MIN)) {
+            break;
+        }
+
+        clock_step(1000000000);
+    }
+
+    g_assert(i > 0);
+}
+
+static void rtc_test_intr_hour(void)
+{
+    int i;
+
+    rtc_start();
+
+    g_assert(!get_irq(A369_FTRTC011_IRQ_HOUR));
+
+    for (i = 3600 + 2; i > 0; i--) {
+
+        if (rtc_get_irq(A369_FTRTC011_IRQ_HOUR)) {
+            break;
+        }
+
+        clock_step(1000000000);
+    }
+
+    g_assert(i > 0);
+}
+
+static void set_time(int h, int m, int s)
+{
+    rtc_write(REG_WDAY, rtc_read(REG_DAY));
+    rtc_write(REG_WHOUR, h);
+    rtc_write(REG_WMIN, m);
+    rtc_write(REG_WSEC, s);
+
+    /* update rtc counter */
+    rtc_write(REG_CR, rtc_read(REG_CR) | CR_LOAD);
+}
+
+#define assert_time(h, m, s) \
+    do { \
+        g_assert_cmpint(rtc_read(REG_HOUR), ==, h); \
+        g_assert_cmpint(rtc_read(REG_MIN), ==, m); \
+        g_assert_cmpint(rtc_read(REG_SEC), ==, s); \
+    } while (0)
+
+static void rtc_test_basic_24h(void)
+{
+    rtc_start();
+
+    /* set decimal 24 hour mode */
+    set_time(9, 59, 0);
+    clock_step(1000000000LL);
+    assert_time(9, 59, 1);
+    clock_step(59000000000LL);
+    assert_time(10, 0, 0);
+
+    /* test hour wraparound */
+    set_time(9, 59, 0);
+    clock_step(60000000000LL);
+    assert_time(10, 0, 0);
+
+    /* test day wraparound */
+    set_time(23, 59, 0);
+    clock_step(60000000000LL);
+    assert_time(0, 0, 0);
+}
+
+static void rtc_test_set_year(void)
+{
+    char t[256];
+    int i, year;
+    time_t ts;
+    struct tm date;
+
+    rtc_start();
+
+    for (i = 0; i < 1000; ++i) {
+        year = CFG_BASEYEAR + g_test_rand_int_range(0, 20);
+
+        date.tm_isdst = -1;
+        date.tm_year  = year - 1900;
+        date.tm_mon   = g_test_rand_int_range(0, 11);
+        date.tm_mday  = g_test_rand_int_range(1, 25);
+        date.tm_hour  = g_test_rand_int_range(0, 23);
+        date.tm_min   = g_test_rand_int_range(0, 59);
+        date.tm_sec   = g_test_rand_int_range(0, 59);
+
+        asctime_r(&date, t);
+
+        ts = mktime(&date) - ts_base;
+        rtc_write(REG_WDAY, ts / 86400LL);
+        ts %= 86400LL;
+        rtc_write(REG_WHOUR, ts / 3600LL);
+        ts %= 3600LL;
+        rtc_write(REG_WMIN, ts / 60LL);
+        ts %= 60LL;
+        rtc_write(REG_WSEC, ts);
+
+        /* update rtc counter */
+        rtc_write(REG_CR, rtc_read(REG_CR) | CR_LOAD);
+
+        rtc_get_datetime(&date);
+
+        asctime_r(&date, t);
+
+        g_assert(date.tm_year == year - 1900);
+    }
+}
+
+/* success if no crash or abort */
+static void rtc_test_fuzz(void)
+{
+    unsigned int i;
+
+    rtc_start();
+
+    for (i = 0; i < 1000; i++) {
+        uint8_t reg, val;
+
+        reg = (uint8_t)g_test_rand_int_range(0x00, 0x34) & 0xfc;
+        val = (uint8_t)g_test_rand_int_range(0, 256);
+
+        rtc_write(reg, val);
+        rtc_read(reg);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    QTestState *s = NULL;
+    int ret;
+
+    g_test_init(&argc, &argv, NULL);
+
+    s = qtest_start("-M a369 -nographic -kernel /dev/zero -rtc clock=vm");
+    qtest_irq_intercept_in(s, "ftrtc011");
+    qtest_add_func("/rtc/check-time", rtc_test_check_time);
+    qtest_add_func("/rtc/interrupt/alarm", rtc_test_intr_alarm);
+    qtest_add_func("/rtc/interrupt/sec", rtc_test_intr_sec);
+    qtest_add_func("/rtc/interrupt/min", rtc_test_intr_min);
+    qtest_add_func("/rtc/interrupt/hour", rtc_test_intr_hour);
+    qtest_add_func("/rtc/basic-24h", rtc_test_basic_24h);
+    qtest_add_func("/rtc/set-year", rtc_test_set_year);
+    qtest_add_func("/rtc/fuzz-registers", rtc_test_fuzz);
+    ret = g_test_run();
+
+    if (s) {
+        qtest_quit(s);
+    }
+
+    return ret;
+}
-- 
1.7.9.5

  parent reply	other threads:[~2013-03-25 12:23 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 01/24] target-arm: add Faraday ARMv5TE processors support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 02/24] hw/arm: add Faraday a369 SoC platform support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 03/24] hw/arm: add FTINTC020 interrupt controller support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 04/24] hw/arm: add FTAHBC020 AHB " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 05/24] hw/arm: add FTDDRII030 DDRII " Kuo-Jung Su
2013-03-28  0:09   ` Peter Crosthwaite
2013-03-28  3:24     ` Kuo-Jung Su
2013-03-28  3:58       ` Peter Crosthwaite
2013-03-28  5:28         ` Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 06/24] hw/arm: add FTPWMTMR010 timer support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 07/24] hw/arm: add FTWDT010 watchdog " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 08/24] hw/arm: add FTRTC011 RTC " Kuo-Jung Su
2013-03-25 12:09 ` Kuo-Jung Su [this message]
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support Kuo-Jung Su
2013-03-29  0:22   ` Peter Crosthwaite
2013-03-29  7:23     ` Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 11/24] hw/arm: add FTAPBBRG020 APB " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 12/24] hw/arm: add FTNANDC021 nand flash controller support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 13/24] hw/arm: add FTI2C010 I2C " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 14/24] hw: Add AudioCodecClass for wm87xx audio class abstration Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 15/24] hw: add WM8731 audio codec support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 16/24] The FTSSP010 is a multi-function synchronous serial port interface controller which supports SSP, SPI, I2S, AC97 and SPDIF Kuo-Jung Su
2013-03-31  2:39   ` Peter Crosthwaite
2013-04-01  1:18     ` Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 17/24] qemu/bitops.h: add the bit ordering reversal functions Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 18/24] hw/arm: add FTGMAC100 1Gbps ethernet support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 19/24] hw/arm: add FTLCDC200 LCD controller support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 20/24] hw/arm: add FTTSC010 touchscreen " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 21/24] hw/arm: add FTSDC010 MMC/SD " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 22/24] hw/arm: add FTMAC110 10/100Mbps ethernet support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 23/24] hw/arm: add FTTMR010 timer support Kuo-Jung Su
2013-03-25 12:10 ` [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash controller support Kuo-Jung Su
2013-03-29  0:02   ` Peter Crosthwaite
2013-03-29  7:15     ` Kuo-Jung Su

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1364213400-10266-10-git-send-email-dantesu@gmail.com \
    --to=dantesu@gmail.com \
    --cc=afaerber@suse.de \
    --cc=blauwirbel@gmail.com \
    --cc=chenwj@iis.sinica.edu.tw \
    --cc=dantesu@faraday-tech.com \
    --cc=fred.konrad@greensocs.com \
    --cc=i.mitsyanko@samsung.com \
    --cc=paul@codesourcery.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).