aboutsummaryrefslogtreecommitdiff
path: root/tests/qtest/stm32l4x5_rcc-test.c
blob: d927c655d138b4117657b148644b316db1f0b771 (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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
 * QTest testcase for STM32L4x5_RCC
 *
 * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
 * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * 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 "qemu/osdep.h"
#include "hw/registerfields.h"
#include "libqtest-single.h"
#include "hw/misc/stm32l4x5_rcc_internals.h"

#define RCC_BASE_ADDR 0x40021000
#define NVIC_ISER 0xE000E100
#define NVIC_ISPR 0xE000E200
#define NVIC_ICPR 0xE000E280
#define RCC_IRQ 5

static void enable_nvic_irq(unsigned int n)
{
    writel(NVIC_ISER, 1 << n);
}

static void unpend_nvic_irq(unsigned int n)
{
    writel(NVIC_ICPR, 1 << n);
}

static bool check_nvic_pending(unsigned int n)
{
    return readl(NVIC_ISPR) & (1 << n);
}

static void rcc_writel(unsigned int offset, uint32_t value)
{
    writel(RCC_BASE_ADDR + offset, value);
}

static uint32_t rcc_readl(unsigned int offset)
{
    return readl(RCC_BASE_ADDR + offset);
}

static void test_init_msi(void)
{
    /* MSIRANGE can be set only when MSI is OFF or READY */
    rcc_writel(A_CR, R_CR_MSION_MASK);
    /* Wait until MSI is stable */
    g_assert_true((rcc_readl(A_CR) & R_CR_MSIRDY_MASK) == R_CR_MSIRDY_MASK);
    /* TODO find a way to test MSI value */
}

static void test_set_msi_as_sysclk(void)
{
    /* Clocking from MSI, in case MSI was not the default source */
    rcc_writel(A_CFGR, 0);
    /* Wait until MSI is selected and stable */
    g_assert_true((rcc_readl(A_CFGR) & R_CFGR_SWS_MASK) == 0);
}

static void test_init_pll(void)
{
    uint32_t value;

    /*
     * Update PLL and set MSI as the source clock.
     * PLLM = 1 --> 000
     * PLLN = 40 --> 40
     * PPLLR = 2 --> 00
     * PLLDIV = unused, PLLP = unused (SAI3), PLLQ = unused (48M1)
     * SRC = MSI --> 01
     */
    rcc_writel(A_PLLCFGR, R_PLLCFGR_PLLREN_MASK |
            (40 << R_PLLCFGR_PLLN_SHIFT) |
            (0b01 << R_PLLCFGR_PLLSRC_SHIFT));

    /* PLL activation */
    value = rcc_readl(A_CR);
    rcc_writel(A_CR, value | R_CR_PLLON_MASK);

    /* Waiting for PLL lock. */
    g_assert_true((rcc_readl(A_CR) & R_CR_PLLRDY_MASK) == R_CR_PLLRDY_MASK);

    /* Switches on the PLL clock source */
    value = rcc_readl(A_CFGR);
    rcc_writel(A_CFGR, (value & ~R_CFGR_SW_MASK) |
        (0b11 << R_CFGR_SW_SHIFT));

    /* Wait until SYSCLK is stable. */
    g_assert_true((rcc_readl(A_CFGR) & R_CFGR_SWS_MASK) ==
        (0b11 << R_CFGR_SWS_SHIFT));
}

static void test_activate_lse(void)
{
    /* LSE activation, no LSE Bypass */
    rcc_writel(A_BDCR, R_BDCR_LSEDRV_MASK | R_BDCR_LSEON_MASK);
    g_assert_true((rcc_readl(A_BDCR) & R_BDCR_LSERDY_MASK) == R_BDCR_LSERDY_MASK);
}

static void test_irq(void)
{
    enable_nvic_irq(RCC_IRQ);

    rcc_writel(A_CIER, R_CIER_LSIRDYIE_MASK);
    rcc_writel(A_CSR, R_CSR_LSION_MASK);
    g_assert_true(check_nvic_pending(RCC_IRQ));
    rcc_writel(A_CICR, R_CICR_LSIRDYC_MASK);
    unpend_nvic_irq(RCC_IRQ);

    rcc_writel(A_CIER, R_CIER_LSERDYIE_MASK);
    rcc_writel(A_BDCR, R_BDCR_LSEON_MASK);
    g_assert_true(check_nvic_pending(RCC_IRQ));
    rcc_writel(A_CICR, R_CICR_LSERDYC_MASK);
    unpend_nvic_irq(RCC_IRQ);

    /*
     * MSI has been enabled by previous tests,
     * shouln't generate an interruption.
     */
    rcc_writel(A_CIER, R_CIER_MSIRDYIE_MASK);
    rcc_writel(A_CR, R_CR_MSION_MASK);
    g_assert_false(check_nvic_pending(RCC_IRQ));

    rcc_writel(A_CIER, R_CIER_HSIRDYIE_MASK);
    rcc_writel(A_CR, R_CR_HSION_MASK);
    g_assert_true(check_nvic_pending(RCC_IRQ));
    rcc_writel(A_CICR, R_CICR_HSIRDYC_MASK);
    unpend_nvic_irq(RCC_IRQ);

    rcc_writel(A_CIER, R_CIER_HSERDYIE_MASK);
    rcc_writel(A_CR, R_CR_HSEON_MASK);
    g_assert_true(check_nvic_pending(RCC_IRQ));
    rcc_writel(A_CICR, R_CICR_HSERDYC_MASK);
    unpend_nvic_irq(RCC_IRQ);

    /*
     * PLL has been enabled by previous tests,
     * shouln't generate an interruption.
     */
    rcc_writel(A_CIER, R_CIER_PLLRDYIE_MASK);
    rcc_writel(A_CR, R_CR_PLLON_MASK);
    g_assert_false(check_nvic_pending(RCC_IRQ));

    rcc_writel(A_CIER, R_CIER_PLLSAI1RDYIE_MASK);
    rcc_writel(A_CR, R_CR_PLLSAI1ON_MASK);
    g_assert_true(check_nvic_pending(RCC_IRQ));
    rcc_writel(A_CICR, R_CICR_PLLSAI1RDYC_MASK);
    unpend_nvic_irq(RCC_IRQ);

    rcc_writel(A_CIER, R_CIER_PLLSAI2RDYIE_MASK);
    rcc_writel(A_CR, R_CR_PLLSAI2ON_MASK);
    g_assert_true(check_nvic_pending(RCC_IRQ));
    rcc_writel(A_CICR, R_CICR_PLLSAI2RDYC_MASK);
    unpend_nvic_irq(RCC_IRQ);
}

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

    g_test_init(&argc, &argv, NULL);
    g_test_set_nonfatal_assertions();
    /*
     * These test separately that we can enable the plls, change the sysclk,
     * and enable different devices.
     * They are dependent on one another.
     * We assume that all operations that would take some time to have an effect
     * (e.g. changing the PLL frequency) are done instantaneously.
     */
    qtest_add_func("stm32l4x5/rcc/init_msi", test_init_msi);
    qtest_add_func("stm32l4x5/rcc/set_msi_as_sysclk",
        test_set_msi_as_sysclk);
    qtest_add_func("stm32l4x5/rcc/activate_lse", test_activate_lse);
    qtest_add_func("stm32l4x5/rcc/init_pll", test_init_pll);

    qtest_add_func("stm32l4x5/rcc/irq", test_irq);

    qtest_start("-machine b-l475e-iot01a");
    ret = g_test_run();
    qtest_end();

    return ret;
}