1 /*
2  * QTest testcase for STM32L4x5_RCC
3  *
4  * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
5  * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr>
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  */
12 
13 #include "qemu/osdep.h"
14 #include "hw/registerfields.h"
15 #include "libqtest-single.h"
16 #include "hw/misc/stm32l4x5_rcc_internals.h"
17 
18 #define RCC_BASE_ADDR 0x40021000
19 #define NVIC_ISER 0xE000E100
20 #define NVIC_ISPR 0xE000E200
21 #define NVIC_ICPR 0xE000E280
22 #define RCC_IRQ 5
23 
24 static void enable_nvic_irq(unsigned int n)
25 {
26     writel(NVIC_ISER, 1 << n);
27 }
28 
29 static void unpend_nvic_irq(unsigned int n)
30 {
31     writel(NVIC_ICPR, 1 << n);
32 }
33 
34 static bool check_nvic_pending(unsigned int n)
35 {
36     return readl(NVIC_ISPR) & (1 << n);
37 }
38 
39 static void rcc_writel(unsigned int offset, uint32_t value)
40 {
41     writel(RCC_BASE_ADDR + offset, value);
42 }
43 
44 static uint32_t rcc_readl(unsigned int offset)
45 {
46     return readl(RCC_BASE_ADDR + offset);
47 }
48 
49 static void test_init_msi(void)
50 {
51     /* MSIRANGE can be set only when MSI is OFF or READY */
52     rcc_writel(A_CR, R_CR_MSION_MASK);
53     /* Wait until MSI is stable */
54     g_assert_true((rcc_readl(A_CR) & R_CR_MSIRDY_MASK) == R_CR_MSIRDY_MASK);
55     /* TODO find a way to test MSI value */
56 }
57 
58 static void test_set_msi_as_sysclk(void)
59 {
60     /* Clocking from MSI, in case MSI was not the default source */
61     rcc_writel(A_CFGR, 0);
62     /* Wait until MSI is selected and stable */
63     g_assert_true((rcc_readl(A_CFGR) & R_CFGR_SWS_MASK) == 0);
64 }
65 
66 static void test_init_pll(void)
67 {
68     uint32_t value;
69 
70     /*
71      * Update PLL and set MSI as the source clock.
72      * PLLM = 1 --> 000
73      * PLLN = 40 --> 40
74      * PPLLR = 2 --> 00
75      * PLLDIV = unused, PLLP = unused (SAI3), PLLQ = unused (48M1)
76      * SRC = MSI --> 01
77      */
78     rcc_writel(A_PLLCFGR, R_PLLCFGR_PLLREN_MASK |
79             (40 << R_PLLCFGR_PLLN_SHIFT) |
80             (0b01 << R_PLLCFGR_PLLSRC_SHIFT));
81 
82     /* PLL activation */
83     value = rcc_readl(A_CR);
84     rcc_writel(A_CR, value | R_CR_PLLON_MASK);
85 
86     /* Waiting for PLL lock. */
87     g_assert_true((rcc_readl(A_CR) & R_CR_PLLRDY_MASK) == R_CR_PLLRDY_MASK);
88 
89     /* Switches on the PLL clock source */
90     value = rcc_readl(A_CFGR);
91     rcc_writel(A_CFGR, (value & ~R_CFGR_SW_MASK) |
92         (0b11 << R_CFGR_SW_SHIFT));
93 
94     /* Wait until SYSCLK is stable. */
95     g_assert_true((rcc_readl(A_CFGR) & R_CFGR_SWS_MASK) ==
96         (0b11 << R_CFGR_SWS_SHIFT));
97 }
98 
99 static void test_activate_lse(void)
100 {
101     /* LSE activation, no LSE Bypass */
102     rcc_writel(A_BDCR, R_BDCR_LSEDRV_MASK | R_BDCR_LSEON_MASK);
103     g_assert_true((rcc_readl(A_BDCR) & R_BDCR_LSERDY_MASK) == R_BDCR_LSERDY_MASK);
104 }
105 
106 static void test_irq(void)
107 {
108     enable_nvic_irq(RCC_IRQ);
109 
110     rcc_writel(A_CIER, R_CIER_LSIRDYIE_MASK);
111     rcc_writel(A_CSR, R_CSR_LSION_MASK);
112     g_assert_true(check_nvic_pending(RCC_IRQ));
113     rcc_writel(A_CICR, R_CICR_LSIRDYC_MASK);
114     unpend_nvic_irq(RCC_IRQ);
115 
116     rcc_writel(A_CIER, R_CIER_LSERDYIE_MASK);
117     rcc_writel(A_BDCR, R_BDCR_LSEON_MASK);
118     g_assert_true(check_nvic_pending(RCC_IRQ));
119     rcc_writel(A_CICR, R_CICR_LSERDYC_MASK);
120     unpend_nvic_irq(RCC_IRQ);
121 
122     /*
123      * MSI has been enabled by previous tests,
124      * shouln't generate an interruption.
125      */
126     rcc_writel(A_CIER, R_CIER_MSIRDYIE_MASK);
127     rcc_writel(A_CR, R_CR_MSION_MASK);
128     g_assert_false(check_nvic_pending(RCC_IRQ));
129 
130     rcc_writel(A_CIER, R_CIER_HSIRDYIE_MASK);
131     rcc_writel(A_CR, R_CR_HSION_MASK);
132     g_assert_true(check_nvic_pending(RCC_IRQ));
133     rcc_writel(A_CICR, R_CICR_HSIRDYC_MASK);
134     unpend_nvic_irq(RCC_IRQ);
135 
136     rcc_writel(A_CIER, R_CIER_HSERDYIE_MASK);
137     rcc_writel(A_CR, R_CR_HSEON_MASK);
138     g_assert_true(check_nvic_pending(RCC_IRQ));
139     rcc_writel(A_CICR, R_CICR_HSERDYC_MASK);
140     unpend_nvic_irq(RCC_IRQ);
141 
142     /*
143      * PLL has been enabled by previous tests,
144      * shouln't generate an interruption.
145      */
146     rcc_writel(A_CIER, R_CIER_PLLRDYIE_MASK);
147     rcc_writel(A_CR, R_CR_PLLON_MASK);
148     g_assert_false(check_nvic_pending(RCC_IRQ));
149 
150     rcc_writel(A_CIER, R_CIER_PLLSAI1RDYIE_MASK);
151     rcc_writel(A_CR, R_CR_PLLSAI1ON_MASK);
152     g_assert_true(check_nvic_pending(RCC_IRQ));
153     rcc_writel(A_CICR, R_CICR_PLLSAI1RDYC_MASK);
154     unpend_nvic_irq(RCC_IRQ);
155 
156     rcc_writel(A_CIER, R_CIER_PLLSAI2RDYIE_MASK);
157     rcc_writel(A_CR, R_CR_PLLSAI2ON_MASK);
158     g_assert_true(check_nvic_pending(RCC_IRQ));
159     rcc_writel(A_CICR, R_CICR_PLLSAI2RDYC_MASK);
160     unpend_nvic_irq(RCC_IRQ);
161 }
162 
163 int main(int argc, char **argv)
164 {
165     int ret;
166 
167     g_test_init(&argc, &argv, NULL);
168     g_test_set_nonfatal_assertions();
169     /*
170      * These test separately that we can enable the plls, change the sysclk,
171      * and enable different devices.
172      * They are dependent on one another.
173      * We assume that all operations that would take some time to have an effect
174      * (e.g. changing the PLL frequency) are done instantaneously.
175      */
176     qtest_add_func("stm32l4x5/rcc/init_msi", test_init_msi);
177     qtest_add_func("stm32l4x5/rcc/set_msi_as_sysclk",
178         test_set_msi_as_sysclk);
179     qtest_add_func("stm32l4x5/rcc/activate_lse", test_activate_lse);
180     qtest_add_func("stm32l4x5/rcc/init_pll", test_init_pll);
181 
182     qtest_add_func("stm32l4x5/rcc/irq", test_irq);
183 
184     qtest_start("-machine b-l475e-iot01a");
185     ret = g_test_run();
186     qtest_end();
187 
188     return ret;
189 }
190