aboutsummaryrefslogtreecommitdiff
path: root/tests/qtest/sse-timer-test.c
blob: fa49b8fb61aebd4a0194a8020f3828803f269e45 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * QTest testcase for the SSE timer device
 *
 * Copyright (c) 2021 Linaro Limited
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 */

#include "qemu/osdep.h"
#include "libqtest-single.h"

/*
 * SSE-123/SSE-300 timer in the mps3-an547 board, where it is driven
 * at 32MHz, so 31.25ns per tick.
 */
#define TIMER_BASE 0x48000000

/* PERIPHNSPPC0 register in the SSE-300 Secure Access Configuration block */
#define PERIPHNSPPC0 (0x50080000 + 0x70)

/* 4 ticks in nanoseconds (so we can work in integers) */
#define FOUR_TICKS 125

#define CNTPCT_LO 0
#define CNTPCT_HI 4
#define CNTFRQ 0x10
#define CNTP_CVAL_LO 0x20
#define CNTP_CVAL_HI 0x24
#define CNTP_TVAL 0x28
#define CNTP_CTL 0x2c
#define CNTP_AIVAL_LO 0x40
#define CNTP_AIVAL_HI 0x44
#define CNTP_AIVAL_RELOAD 0x48
#define CNTP_AIVAL_CTL 0x4c

static void clock_step_ticks(uint64_t ticks)
{
    /*
     * Advance the qtest clock by however many nanoseconds we
     * need to move the timer forward the specified number of ticks.
     * ticks must be a multiple of 4, so we get a whole number of ns.
     */
    assert(!(ticks & 3));
    clock_step(FOUR_TICKS * (ticks >> 2));
}

static void test_timer(void)
{
    /*
     * The timer is behind a Peripheral Protection Controller, and
     * qtest accesses are always non-secure (no memory attributes),
     * so we must program the PPC to accept NS transactions.  TIMER0
     * is on port 0 of PPC0, controlled by bit 0 of this register.
     */
    writel(PERIPHNSPPC0, 1);

    /* Timer starts disabled and with a counter of 0 */
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 0);
    g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 0);
    g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0);

    /* Turn it on */
    writel(TIMER_BASE + CNTP_CTL, 1);

    /* Is the timer ticking? */
    clock_step_ticks(100);
    g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 100);
    g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0);

    /* Set the CompareValue to 4000 ticks */
    writel(TIMER_BASE + CNTP_CVAL_LO, 4000);
    writel(TIMER_BASE + CNTP_CVAL_HI, 0);

    /* Check TVAL view of the counter */
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_TVAL), ==, 3900);

    /* Advance to the CompareValue mark and check ISTATUS is set */
    clock_step_ticks(3900);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_TVAL), ==, 0);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);

    /* Now exercise the auto-reload part of the timer */
    writel(TIMER_BASE + CNTP_AIVAL_RELOAD, 200);
    writel(TIMER_BASE + CNTP_AIVAL_CTL, 1);

    /* Check AIVAL was reloaded and that ISTATUS is now clear */
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4200);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);

    /*
     * Check that when we advance forward to the reload time the interrupt
     * fires and the value reloads
     */
    clock_step_ticks(100);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
    clock_step_ticks(100);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4400);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0);

    clock_step_ticks(100);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
    /* Check that writing 0 to CLR clears the interrupt */
    writel(TIMER_BASE + CNTP_AIVAL_CTL, 1);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
    /* Check that when we move forward to the reload time it fires again */
    clock_step_ticks(100);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4600);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0);

    /*
     * Step the clock far enough that we overflow the low half of the
     * CNTPCT and AIVAL registers, and check that their high halves
     * give the right values. We do the forward movement in
     * non-autoinc mode because otherwise it takes forever as the
     * timer has to emulate all the 'reload at t + N, t + 2N, etc'
     * steps.
     */
    writel(TIMER_BASE + CNTP_AIVAL_CTL, 0);
    clock_step_ticks(0x42ULL << 32);
    g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 4400);
    g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0x42);

    /* Turn on the autoinc again to check AIVAL_HI */
    writel(TIMER_BASE + CNTP_AIVAL_CTL, 1);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4600);
    g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0x42);

    /* Turn off the timer */
    writel(TIMER_BASE + CNTP_CTL, 0);
}

int main(int argc, char **argv)
{
    int r;

    g_test_init(&argc, &argv, NULL);

    qtest_start("-machine mps3-an547");

    qtest_add_func("/sse-timer/timer", test_timer);

    r = g_test_run();

    qtest_end();

    return r;
}