1ddcc4b4bSInès Varhol /*
2ddcc4b4bSInès Varhol  * QTest testcase for STM32L4x5_GPIO
3ddcc4b4bSInès Varhol  *
4ddcc4b4bSInès Varhol  * Copyright (c) 2024 Arnaud Minier <arnaud.minier@telecom-paris.fr>
5ddcc4b4bSInès Varhol  * Copyright (c) 2024 Inès Varhol <ines.varhol@telecom-paris.fr>
6ddcc4b4bSInès Varhol  *
7ddcc4b4bSInès Varhol  * This work is licensed under the terms of the GNU GPL, version 2 or later.
8ddcc4b4bSInès Varhol  * See the COPYING file in the top-level directory.
9ddcc4b4bSInès Varhol  */
10ddcc4b4bSInès Varhol 
11ddcc4b4bSInès Varhol #include "qemu/osdep.h"
12ddcc4b4bSInès Varhol #include "libqtest-single.h"
13ddcc4b4bSInès Varhol 
14ddcc4b4bSInès Varhol #define GPIO_BASE_ADDR 0x48000000
15ddcc4b4bSInès Varhol #define GPIO_SIZE      0x400
16ddcc4b4bSInès Varhol #define NUM_GPIOS      8
17ddcc4b4bSInès Varhol #define NUM_GPIO_PINS  16
18ddcc4b4bSInès Varhol 
19ddcc4b4bSInès Varhol #define GPIO_A 0x48000000
20ddcc4b4bSInès Varhol #define GPIO_B 0x48000400
21ddcc4b4bSInès Varhol #define GPIO_C 0x48000800
22ddcc4b4bSInès Varhol #define GPIO_D 0x48000C00
23ddcc4b4bSInès Varhol #define GPIO_E 0x48001000
24ddcc4b4bSInès Varhol #define GPIO_F 0x48001400
25ddcc4b4bSInès Varhol #define GPIO_G 0x48001800
26ddcc4b4bSInès Varhol #define GPIO_H 0x48001C00
27ddcc4b4bSInès Varhol 
28ddcc4b4bSInès Varhol #define MODER 0x00
29ddcc4b4bSInès Varhol #define OTYPER 0x04
30ddcc4b4bSInès Varhol #define PUPDR 0x0C
31ddcc4b4bSInès Varhol #define IDR 0x10
32ddcc4b4bSInès Varhol #define ODR 0x14
33ddcc4b4bSInès Varhol #define BSRR 0x18
34ddcc4b4bSInès Varhol #define BRR 0x28
35ddcc4b4bSInès Varhol 
36ddcc4b4bSInès Varhol #define MODER_INPUT 0
37ddcc4b4bSInès Varhol #define MODER_OUTPUT 1
38ddcc4b4bSInès Varhol 
39ddcc4b4bSInès Varhol #define PUPDR_NONE 0
40ddcc4b4bSInès Varhol #define PUPDR_PULLUP 1
41ddcc4b4bSInès Varhol #define PUPDR_PULLDOWN 2
42ddcc4b4bSInès Varhol 
43ddcc4b4bSInès Varhol #define OTYPER_PUSH_PULL 0
44ddcc4b4bSInès Varhol #define OTYPER_OPEN_DRAIN 1
45ddcc4b4bSInès Varhol 
46ddcc4b4bSInès Varhol const uint32_t moder_reset[NUM_GPIOS] = {
47ddcc4b4bSInès Varhol     0xABFFFFFF,
48ddcc4b4bSInès Varhol     0xFFFFFEBF,
49ddcc4b4bSInès Varhol     0xFFFFFFFF,
50ddcc4b4bSInès Varhol     0xFFFFFFFF,
51ddcc4b4bSInès Varhol     0xFFFFFFFF,
52ddcc4b4bSInès Varhol     0xFFFFFFFF,
53ddcc4b4bSInès Varhol     0xFFFFFFFF,
54ddcc4b4bSInès Varhol     0x0000000F
55ddcc4b4bSInès Varhol };
56ddcc4b4bSInès Varhol 
57ddcc4b4bSInès Varhol const uint32_t pupdr_reset[NUM_GPIOS] = {
58ddcc4b4bSInès Varhol     0x64000000,
59ddcc4b4bSInès Varhol     0x00000100,
60ddcc4b4bSInès Varhol     0x00000000,
61ddcc4b4bSInès Varhol     0x00000000,
62ddcc4b4bSInès Varhol     0x00000000,
63ddcc4b4bSInès Varhol     0x00000000,
64ddcc4b4bSInès Varhol     0x00000000,
65ddcc4b4bSInès Varhol     0x00000000
66ddcc4b4bSInès Varhol };
67ddcc4b4bSInès Varhol 
68ddcc4b4bSInès Varhol const uint32_t idr_reset[NUM_GPIOS] = {
69ddcc4b4bSInès Varhol     0x0000A000,
70ddcc4b4bSInès Varhol     0x00000010,
71ddcc4b4bSInès Varhol     0x00000000,
72ddcc4b4bSInès Varhol     0x00000000,
73ddcc4b4bSInès Varhol     0x00000000,
74ddcc4b4bSInès Varhol     0x00000000,
75ddcc4b4bSInès Varhol     0x00000000,
76ddcc4b4bSInès Varhol     0x00000000
77ddcc4b4bSInès Varhol };
78ddcc4b4bSInès Varhol 
79*27c335a4SCédric Le Goater #define PIN_MASK        0xF
80*27c335a4SCédric Le Goater #define GPIO_ADDR_MASK  (~(GPIO_SIZE - 1))
81*27c335a4SCédric Le Goater 
82*27c335a4SCédric Le Goater static inline void *test_data(uint32_t gpio_addr, uint8_t pin)
83*27c335a4SCédric Le Goater {
84*27c335a4SCédric Le Goater     return (void *)(uintptr_t)((gpio_addr & GPIO_ADDR_MASK) | (pin & PIN_MASK));
85*27c335a4SCédric Le Goater }
86*27c335a4SCédric Le Goater 
87*27c335a4SCédric Le Goater #define test_gpio_addr(data)      ((uintptr_t)(data) & GPIO_ADDR_MASK)
88*27c335a4SCédric Le Goater #define test_pin(data)            ((uintptr_t)(data) & PIN_MASK)
89*27c335a4SCédric Le Goater 
90ddcc4b4bSInès Varhol static uint32_t gpio_readl(unsigned int gpio, unsigned int offset)
91ddcc4b4bSInès Varhol {
92ddcc4b4bSInès Varhol     return readl(gpio + offset);
93ddcc4b4bSInès Varhol }
94ddcc4b4bSInès Varhol 
95ddcc4b4bSInès Varhol static void gpio_writel(unsigned int gpio, unsigned int offset, uint32_t value)
96ddcc4b4bSInès Varhol {
97ddcc4b4bSInès Varhol     writel(gpio + offset, value);
98ddcc4b4bSInès Varhol }
99ddcc4b4bSInès Varhol 
100ddcc4b4bSInès Varhol static void gpio_set_bit(unsigned int gpio, unsigned int reg,
101ddcc4b4bSInès Varhol                          unsigned int pin, uint32_t value)
102ddcc4b4bSInès Varhol {
103ddcc4b4bSInès Varhol     uint32_t mask = 0xFFFFFFFF & ~(0x1 << pin);
104ddcc4b4bSInès Varhol     gpio_writel(gpio, reg, (gpio_readl(gpio, reg) & mask) | value << pin);
105ddcc4b4bSInès Varhol }
106ddcc4b4bSInès Varhol 
107ddcc4b4bSInès Varhol static void gpio_set_2bits(unsigned int gpio, unsigned int reg,
108ddcc4b4bSInès Varhol                            unsigned int pin, uint32_t value)
109ddcc4b4bSInès Varhol {
110ddcc4b4bSInès Varhol     uint32_t offset = 2 * pin;
111ddcc4b4bSInès Varhol     uint32_t mask = 0xFFFFFFFF & ~(0x3 << offset);
112ddcc4b4bSInès Varhol     gpio_writel(gpio, reg, (gpio_readl(gpio, reg) & mask) | value << offset);
113ddcc4b4bSInès Varhol }
114ddcc4b4bSInès Varhol 
115ddcc4b4bSInès Varhol static unsigned int get_gpio_id(uint32_t gpio_addr)
116ddcc4b4bSInès Varhol {
117ddcc4b4bSInès Varhol     return (gpio_addr - GPIO_BASE_ADDR) / GPIO_SIZE;
118ddcc4b4bSInès Varhol }
119ddcc4b4bSInès Varhol 
120ddcc4b4bSInès Varhol static void gpio_set_irq(unsigned int gpio, int num, int level)
121ddcc4b4bSInès Varhol {
122ddcc4b4bSInès Varhol     g_autofree char *name = g_strdup_printf("/machine/soc/gpio%c",
123ddcc4b4bSInès Varhol                                             get_gpio_id(gpio) + 'a');
124ddcc4b4bSInès Varhol     qtest_set_irq_in(global_qtest, name, NULL, num, level);
125ddcc4b4bSInès Varhol }
126ddcc4b4bSInès Varhol 
127ddcc4b4bSInès Varhol static void disconnect_all_pins(unsigned int gpio)
128ddcc4b4bSInès Varhol {
129ddcc4b4bSInès Varhol     g_autofree char *path = g_strdup_printf("/machine/soc/gpio%c",
130ddcc4b4bSInès Varhol                                             get_gpio_id(gpio) + 'a');
131ddcc4b4bSInès Varhol     QDict *r;
132ddcc4b4bSInès Varhol 
133ddcc4b4bSInès Varhol     r = qtest_qmp(global_qtest, "{ 'execute': 'qom-set', 'arguments': "
134ddcc4b4bSInès Varhol         "{ 'path': %s, 'property': 'disconnected-pins', 'value': %d } }",
135ddcc4b4bSInès Varhol         path, 0xFFFF);
136ddcc4b4bSInès Varhol     g_assert_false(qdict_haskey(r, "error"));
137ddcc4b4bSInès Varhol     qobject_unref(r);
138ddcc4b4bSInès Varhol }
139ddcc4b4bSInès Varhol 
140ddcc4b4bSInès Varhol static uint32_t get_disconnected_pins(unsigned int gpio)
141ddcc4b4bSInès Varhol {
142ddcc4b4bSInès Varhol     g_autofree char *path = g_strdup_printf("/machine/soc/gpio%c",
143ddcc4b4bSInès Varhol                                             get_gpio_id(gpio) + 'a');
144ddcc4b4bSInès Varhol     uint32_t disconnected_pins = 0;
145ddcc4b4bSInès Varhol     QDict *r;
146ddcc4b4bSInès Varhol 
147ddcc4b4bSInès Varhol     r = qtest_qmp(global_qtest, "{ 'execute': 'qom-get', 'arguments':"
148ddcc4b4bSInès Varhol         " { 'path': %s, 'property': 'disconnected-pins'} }", path);
149ddcc4b4bSInès Varhol     g_assert_false(qdict_haskey(r, "error"));
150ddcc4b4bSInès Varhol     disconnected_pins = qdict_get_int(r, "return");
151ddcc4b4bSInès Varhol     qobject_unref(r);
152ddcc4b4bSInès Varhol     return disconnected_pins;
153ddcc4b4bSInès Varhol }
154ddcc4b4bSInès Varhol 
155ddcc4b4bSInès Varhol static uint32_t reset(uint32_t gpio, unsigned int offset)
156ddcc4b4bSInès Varhol {
157ddcc4b4bSInès Varhol     switch (offset) {
158ddcc4b4bSInès Varhol     case MODER:
159ddcc4b4bSInès Varhol         return moder_reset[get_gpio_id(gpio)];
160ddcc4b4bSInès Varhol     case PUPDR:
161ddcc4b4bSInès Varhol         return pupdr_reset[get_gpio_id(gpio)];
162ddcc4b4bSInès Varhol     case IDR:
163ddcc4b4bSInès Varhol         return idr_reset[get_gpio_id(gpio)];
164ddcc4b4bSInès Varhol     }
165ddcc4b4bSInès Varhol     return 0x0;
166ddcc4b4bSInès Varhol }
167ddcc4b4bSInès Varhol 
168ddcc4b4bSInès Varhol static void system_reset(void)
169ddcc4b4bSInès Varhol {
170ddcc4b4bSInès Varhol     QDict *r;
171ddcc4b4bSInès Varhol     r = qtest_qmp(global_qtest, "{'execute': 'system_reset'}");
172ddcc4b4bSInès Varhol     g_assert_false(qdict_haskey(r, "error"));
173ddcc4b4bSInès Varhol     qobject_unref(r);
174ddcc4b4bSInès Varhol }
175ddcc4b4bSInès Varhol 
176ddcc4b4bSInès Varhol static void test_idr_reset_value(void)
177ddcc4b4bSInès Varhol {
178ddcc4b4bSInès Varhol     /*
179ddcc4b4bSInès Varhol      * Checks that the values in MODER, OTYPER, PUPDR and ODR
180ddcc4b4bSInès Varhol      * after reset are correct, and that the value in IDR is
181ddcc4b4bSInès Varhol      * coherent.
182ddcc4b4bSInès Varhol      * Since AF and analog modes aren't implemented, IDR reset
183ddcc4b4bSInès Varhol      * values aren't the same as with a real board.
184ddcc4b4bSInès Varhol      *
185ddcc4b4bSInès Varhol      * Register IDR contains the actual values of all GPIO pins.
186ddcc4b4bSInès Varhol      * Its value depends on the pins' configuration
187ddcc4b4bSInès Varhol      * (intput/output/analog : register MODER, push-pull/open-drain :
188ddcc4b4bSInès Varhol      * register OTYPER, pull-up/pull-down/none : register PUPDR)
189ddcc4b4bSInès Varhol      * and on the values stored in register ODR
190ddcc4b4bSInès Varhol      * (in case the pin is in output mode).
191ddcc4b4bSInès Varhol      */
192ddcc4b4bSInès Varhol 
193ddcc4b4bSInès Varhol     gpio_writel(GPIO_A, MODER, 0xDEADBEEF);
194ddcc4b4bSInès Varhol     gpio_writel(GPIO_A, ODR, 0xDEADBEEF);
195ddcc4b4bSInès Varhol     gpio_writel(GPIO_A, OTYPER, 0xDEADBEEF);
196ddcc4b4bSInès Varhol     gpio_writel(GPIO_A, PUPDR, 0xDEADBEEF);
197ddcc4b4bSInès Varhol 
198ddcc4b4bSInès Varhol     gpio_writel(GPIO_B, MODER, 0xDEADBEEF);
199ddcc4b4bSInès Varhol     gpio_writel(GPIO_B, ODR, 0xDEADBEEF);
200ddcc4b4bSInès Varhol     gpio_writel(GPIO_B, OTYPER, 0xDEADBEEF);
201ddcc4b4bSInès Varhol     gpio_writel(GPIO_B, PUPDR, 0xDEADBEEF);
202ddcc4b4bSInès Varhol 
203ddcc4b4bSInès Varhol     gpio_writel(GPIO_C, MODER, 0xDEADBEEF);
204ddcc4b4bSInès Varhol     gpio_writel(GPIO_C, ODR, 0xDEADBEEF);
205ddcc4b4bSInès Varhol     gpio_writel(GPIO_C, OTYPER, 0xDEADBEEF);
206ddcc4b4bSInès Varhol     gpio_writel(GPIO_C, PUPDR, 0xDEADBEEF);
207ddcc4b4bSInès Varhol 
208ddcc4b4bSInès Varhol     gpio_writel(GPIO_H, MODER, 0xDEADBEEF);
209ddcc4b4bSInès Varhol     gpio_writel(GPIO_H, ODR, 0xDEADBEEF);
210ddcc4b4bSInès Varhol     gpio_writel(GPIO_H, OTYPER, 0xDEADBEEF);
211ddcc4b4bSInès Varhol     gpio_writel(GPIO_H, PUPDR, 0xDEADBEEF);
212ddcc4b4bSInès Varhol 
213ddcc4b4bSInès Varhol     system_reset();
214ddcc4b4bSInès Varhol 
215ddcc4b4bSInès Varhol     uint32_t moder = gpio_readl(GPIO_A, MODER);
216ddcc4b4bSInès Varhol     uint32_t odr = gpio_readl(GPIO_A, ODR);
217ddcc4b4bSInès Varhol     uint32_t otyper = gpio_readl(GPIO_A, OTYPER);
218ddcc4b4bSInès Varhol     uint32_t pupdr = gpio_readl(GPIO_A, PUPDR);
219ddcc4b4bSInès Varhol     uint32_t idr = gpio_readl(GPIO_A, IDR);
220ddcc4b4bSInès Varhol     /* 15: AF, 14: AF, 13: AF, 12: Analog ... */
221ddcc4b4bSInès Varhol     /* here AF is the same as Analog and Input mode */
222ddcc4b4bSInès Varhol     g_assert_cmphex(moder, ==, reset(GPIO_A, MODER));
223ddcc4b4bSInès Varhol     g_assert_cmphex(odr, ==, reset(GPIO_A, ODR));
224ddcc4b4bSInès Varhol     g_assert_cmphex(otyper, ==, reset(GPIO_A, OTYPER));
225ddcc4b4bSInès Varhol     /* 15: pull-up, 14: pull-down, 13: pull-up, 12: neither ... */
226ddcc4b4bSInès Varhol     g_assert_cmphex(pupdr, ==, reset(GPIO_A, PUPDR));
227ddcc4b4bSInès Varhol     /* 15 : 1, 14: 0, 13: 1, 12 : reset value ... */
228ddcc4b4bSInès Varhol     g_assert_cmphex(idr, ==, reset(GPIO_A, IDR));
229ddcc4b4bSInès Varhol 
230ddcc4b4bSInès Varhol     moder = gpio_readl(GPIO_B, MODER);
231ddcc4b4bSInès Varhol     odr = gpio_readl(GPIO_B, ODR);
232ddcc4b4bSInès Varhol     otyper = gpio_readl(GPIO_B, OTYPER);
233ddcc4b4bSInès Varhol     pupdr = gpio_readl(GPIO_B, PUPDR);
234ddcc4b4bSInès Varhol     idr = gpio_readl(GPIO_B, IDR);
235ddcc4b4bSInès Varhol     /* ... 5: Analog, 4: AF, 3: AF, 2: Analog ... */
236ddcc4b4bSInès Varhol     /* here AF is the same as Analog and Input mode */
237ddcc4b4bSInès Varhol     g_assert_cmphex(moder, ==, reset(GPIO_B, MODER));
238ddcc4b4bSInès Varhol     g_assert_cmphex(odr, ==, reset(GPIO_B, ODR));
239ddcc4b4bSInès Varhol     g_assert_cmphex(otyper, ==, reset(GPIO_B, OTYPER));
240ddcc4b4bSInès Varhol     /* ... 5: neither, 4: pull-up, 3: neither ... */
241ddcc4b4bSInès Varhol     g_assert_cmphex(pupdr, ==, reset(GPIO_B, PUPDR));
242ddcc4b4bSInès Varhol     /* ... 5 : reset value, 4 : 1, 3 : reset value ... */
243ddcc4b4bSInès Varhol     g_assert_cmphex(idr, ==, reset(GPIO_B, IDR));
244ddcc4b4bSInès Varhol 
245ddcc4b4bSInès Varhol     moder = gpio_readl(GPIO_C, MODER);
246ddcc4b4bSInès Varhol     odr = gpio_readl(GPIO_C, ODR);
247ddcc4b4bSInès Varhol     otyper = gpio_readl(GPIO_C, OTYPER);
248ddcc4b4bSInès Varhol     pupdr = gpio_readl(GPIO_C, PUPDR);
249ddcc4b4bSInès Varhol     idr = gpio_readl(GPIO_C, IDR);
250ddcc4b4bSInès Varhol     /* Analog, same as Input mode*/
251ddcc4b4bSInès Varhol     g_assert_cmphex(moder, ==, reset(GPIO_C, MODER));
252ddcc4b4bSInès Varhol     g_assert_cmphex(odr, ==, reset(GPIO_C, ODR));
253ddcc4b4bSInès Varhol     g_assert_cmphex(otyper, ==, reset(GPIO_C, OTYPER));
254ddcc4b4bSInès Varhol     /* no pull-up or pull-down */
255ddcc4b4bSInès Varhol     g_assert_cmphex(pupdr, ==, reset(GPIO_C, PUPDR));
256ddcc4b4bSInès Varhol     /* reset value */
257ddcc4b4bSInès Varhol     g_assert_cmphex(idr, ==, reset(GPIO_C, IDR));
258ddcc4b4bSInès Varhol 
259ddcc4b4bSInès Varhol     moder = gpio_readl(GPIO_H, MODER);
260ddcc4b4bSInès Varhol     odr = gpio_readl(GPIO_H, ODR);
261ddcc4b4bSInès Varhol     otyper = gpio_readl(GPIO_H, OTYPER);
262ddcc4b4bSInès Varhol     pupdr = gpio_readl(GPIO_H, PUPDR);
263ddcc4b4bSInès Varhol     idr = gpio_readl(GPIO_H, IDR);
264ddcc4b4bSInès Varhol     /* Analog, same as Input mode */
265ddcc4b4bSInès Varhol     g_assert_cmphex(moder, ==, reset(GPIO_H, MODER));
266ddcc4b4bSInès Varhol     g_assert_cmphex(odr, ==, reset(GPIO_H, ODR));
267ddcc4b4bSInès Varhol     g_assert_cmphex(otyper, ==, reset(GPIO_H, OTYPER));
268ddcc4b4bSInès Varhol     /* no pull-up or pull-down */
269ddcc4b4bSInès Varhol     g_assert_cmphex(pupdr, ==, reset(GPIO_H, PUPDR));
270ddcc4b4bSInès Varhol     /* reset value */
271ddcc4b4bSInès Varhol     g_assert_cmphex(idr, ==, reset(GPIO_H, IDR));
272ddcc4b4bSInès Varhol }
273ddcc4b4bSInès Varhol 
274ddcc4b4bSInès Varhol static void test_gpio_output_mode(const void *data)
275ddcc4b4bSInès Varhol {
276ddcc4b4bSInès Varhol     /*
277ddcc4b4bSInès Varhol      * Checks that setting a bit in ODR sets the corresponding
278ddcc4b4bSInès Varhol      * GPIO line high : it should set the right bit in IDR
279ddcc4b4bSInès Varhol      * and send an irq to syscfg.
280ddcc4b4bSInès Varhol      * Additionally, it checks that values written to ODR
281ddcc4b4bSInès Varhol      * when not in output mode are stored and not discarded.
282ddcc4b4bSInès Varhol      */
283*27c335a4SCédric Le Goater     unsigned int pin = test_pin(data);
284*27c335a4SCédric Le Goater     uint32_t gpio = test_gpio_addr(data);
285ddcc4b4bSInès Varhol     unsigned int gpio_id = get_gpio_id(gpio);
286ddcc4b4bSInès Varhol 
287ddcc4b4bSInès Varhol     qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg");
288ddcc4b4bSInès Varhol 
289ddcc4b4bSInès Varhol     /* Set a bit in ODR and check nothing happens */
290ddcc4b4bSInès Varhol     gpio_set_bit(gpio, ODR, pin, 1);
291ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR));
292ddcc4b4bSInès Varhol     g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin));
293ddcc4b4bSInès Varhol 
294ddcc4b4bSInès Varhol     /* Configure the relevant line as output and check the pin is high */
295ddcc4b4bSInès Varhol     gpio_set_2bits(gpio, MODER, pin, MODER_OUTPUT);
296ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) | (1 << pin));
297ddcc4b4bSInès Varhol     g_assert_true(get_irq(gpio_id * NUM_GPIO_PINS + pin));
298ddcc4b4bSInès Varhol 
299ddcc4b4bSInès Varhol     /* Reset the bit in ODR and check the pin is low */
300ddcc4b4bSInès Varhol     gpio_set_bit(gpio, ODR, pin, 0);
301ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
302ddcc4b4bSInès Varhol     g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin));
303ddcc4b4bSInès Varhol 
304ddcc4b4bSInès Varhol     /* Clean the test */
305ddcc4b4bSInès Varhol     gpio_writel(gpio, ODR, reset(gpio, ODR));
306ddcc4b4bSInès Varhol     gpio_writel(gpio, MODER, reset(gpio, MODER));
307ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR));
308ddcc4b4bSInès Varhol     g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin));
309ddcc4b4bSInès Varhol }
310ddcc4b4bSInès Varhol 
311ddcc4b4bSInès Varhol static void test_gpio_input_mode(const void *data)
312ddcc4b4bSInès Varhol {
313ddcc4b4bSInès Varhol     /*
314ddcc4b4bSInès Varhol      * Test that setting a line high/low externally sets the
315ddcc4b4bSInès Varhol      * corresponding GPIO line high/low : it should set the
316ddcc4b4bSInès Varhol      * right bit in IDR and send an irq to syscfg.
317ddcc4b4bSInès Varhol      */
318*27c335a4SCédric Le Goater     unsigned int pin = test_pin(data);
319*27c335a4SCédric Le Goater     uint32_t gpio = test_gpio_addr(data);
320ddcc4b4bSInès Varhol     unsigned int gpio_id = get_gpio_id(gpio);
321ddcc4b4bSInès Varhol 
322ddcc4b4bSInès Varhol     qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg");
323ddcc4b4bSInès Varhol 
324ddcc4b4bSInès Varhol     /* Configure a line as input, raise it, and check that the pin is high */
325ddcc4b4bSInès Varhol     gpio_set_2bits(gpio, MODER, pin, MODER_INPUT);
326ddcc4b4bSInès Varhol     gpio_set_irq(gpio, pin, 1);
327ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) | (1 << pin));
328ddcc4b4bSInès Varhol     g_assert_true(get_irq(gpio_id * NUM_GPIO_PINS + pin));
329ddcc4b4bSInès Varhol 
330ddcc4b4bSInès Varhol     /* Lower the line and check that the pin is low */
331ddcc4b4bSInès Varhol     gpio_set_irq(gpio, pin, 0);
332ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
333ddcc4b4bSInès Varhol     g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin));
334ddcc4b4bSInès Varhol 
335ddcc4b4bSInès Varhol     /* Clean the test */
336ddcc4b4bSInès Varhol     gpio_writel(gpio, MODER, reset(gpio, MODER));
337ddcc4b4bSInès Varhol     disconnect_all_pins(gpio);
338ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR));
339ddcc4b4bSInès Varhol }
340ddcc4b4bSInès Varhol 
341ddcc4b4bSInès Varhol static void test_pull_up_pull_down(const void *data)
342ddcc4b4bSInès Varhol {
343ddcc4b4bSInès Varhol     /*
344ddcc4b4bSInès Varhol      * Test that a floating pin with pull-up sets the pin
345ddcc4b4bSInès Varhol      * high and vice-versa.
346ddcc4b4bSInès Varhol      */
347*27c335a4SCédric Le Goater     unsigned int pin = test_pin(data);
348*27c335a4SCédric Le Goater     uint32_t gpio = test_gpio_addr(data);
349ddcc4b4bSInès Varhol     unsigned int gpio_id = get_gpio_id(gpio);
350ddcc4b4bSInès Varhol 
351ddcc4b4bSInès Varhol     qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg");
352ddcc4b4bSInès Varhol 
353ddcc4b4bSInès Varhol     /* Configure a line as input with pull-up, check the line is set high */
354ddcc4b4bSInès Varhol     gpio_set_2bits(gpio, MODER, pin, MODER_INPUT);
355ddcc4b4bSInès Varhol     gpio_set_2bits(gpio, PUPDR, pin, PUPDR_PULLUP);
356ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) | (1 << pin));
357ddcc4b4bSInès Varhol     g_assert_true(get_irq(gpio_id * NUM_GPIO_PINS + pin));
358ddcc4b4bSInès Varhol 
359ddcc4b4bSInès Varhol     /* Configure the line with pull-down, check the line is low */
360ddcc4b4bSInès Varhol     gpio_set_2bits(gpio, PUPDR, pin, PUPDR_PULLDOWN);
361ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
362ddcc4b4bSInès Varhol     g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin));
363ddcc4b4bSInès Varhol 
364ddcc4b4bSInès Varhol     /* Clean the test */
365ddcc4b4bSInès Varhol     gpio_writel(gpio, MODER, reset(gpio, MODER));
366ddcc4b4bSInès Varhol     gpio_writel(gpio, PUPDR, reset(gpio, PUPDR));
367ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR));
368ddcc4b4bSInès Varhol }
369ddcc4b4bSInès Varhol 
370ddcc4b4bSInès Varhol static void test_push_pull(const void *data)
371ddcc4b4bSInès Varhol {
372ddcc4b4bSInès Varhol     /*
373ddcc4b4bSInès Varhol      * Test that configuring a line in push-pull output mode
374ddcc4b4bSInès Varhol      * disconnects the pin, that the pin can't be set or reset
375ddcc4b4bSInès Varhol      * externally afterwards.
376ddcc4b4bSInès Varhol      */
377*27c335a4SCédric Le Goater     unsigned int pin = test_pin(data);
378*27c335a4SCédric Le Goater     uint32_t gpio = test_gpio_addr(data);
379ddcc4b4bSInès Varhol     uint32_t gpio2 = GPIO_BASE_ADDR + (GPIO_H - gpio);
380ddcc4b4bSInès Varhol 
381ddcc4b4bSInès Varhol     qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg");
382ddcc4b4bSInès Varhol 
383ddcc4b4bSInès Varhol     /* Setting a line high externally, configuring it in push-pull output */
384ddcc4b4bSInès Varhol     /* And checking the pin was disconnected */
385ddcc4b4bSInès Varhol     gpio_set_irq(gpio, pin, 1);
386ddcc4b4bSInès Varhol     gpio_set_2bits(gpio, MODER, pin, MODER_OUTPUT);
387ddcc4b4bSInès Varhol     g_assert_cmphex(get_disconnected_pins(gpio), ==, 0xFFFF);
388ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
389ddcc4b4bSInès Varhol 
390ddcc4b4bSInès Varhol     /* Setting a line low externally, configuring it in push-pull output */
391ddcc4b4bSInès Varhol     /* And checking the pin was disconnected */
392ddcc4b4bSInès Varhol     gpio_set_irq(gpio2, pin, 0);
393ddcc4b4bSInès Varhol     gpio_set_bit(gpio2, ODR, pin, 1);
394ddcc4b4bSInès Varhol     gpio_set_2bits(gpio2, MODER, pin, MODER_OUTPUT);
395ddcc4b4bSInès Varhol     g_assert_cmphex(get_disconnected_pins(gpio2), ==, 0xFFFF);
396ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio2, IDR), ==, reset(gpio2, IDR) | (1 << pin));
397ddcc4b4bSInès Varhol 
398ddcc4b4bSInès Varhol     /* Trying to set a push-pull output pin, checking it doesn't work */
399ddcc4b4bSInès Varhol     gpio_set_irq(gpio, pin, 1);
400ddcc4b4bSInès Varhol     g_assert_cmphex(get_disconnected_pins(gpio), ==, 0xFFFF);
401ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
402ddcc4b4bSInès Varhol 
403ddcc4b4bSInès Varhol     /* Trying to reset a push-pull output pin, checking it doesn't work */
404ddcc4b4bSInès Varhol     gpio_set_irq(gpio2, pin, 0);
405ddcc4b4bSInès Varhol     g_assert_cmphex(get_disconnected_pins(gpio2), ==, 0xFFFF);
406ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio2, IDR), ==, reset(gpio2, IDR) | (1 << pin));
407ddcc4b4bSInès Varhol 
408ddcc4b4bSInès Varhol     /* Clean the test */
409ddcc4b4bSInès Varhol     gpio_writel(gpio, MODER, reset(gpio, MODER));
410ddcc4b4bSInès Varhol     gpio_writel(gpio2, ODR, reset(gpio2, ODR));
411ddcc4b4bSInès Varhol     gpio_writel(gpio2, MODER, reset(gpio2, MODER));
412ddcc4b4bSInès Varhol }
413ddcc4b4bSInès Varhol 
414ddcc4b4bSInès Varhol static void test_open_drain(const void *data)
415ddcc4b4bSInès Varhol {
416ddcc4b4bSInès Varhol     /*
417ddcc4b4bSInès Varhol      * Test that configuring a line in open-drain output mode
418ddcc4b4bSInès Varhol      * disconnects a pin set high externally and that the pin
419ddcc4b4bSInès Varhol      * can't be set high externally while configured in open-drain.
420ddcc4b4bSInès Varhol      *
421ddcc4b4bSInès Varhol      * However a pin set low externally shouldn't be disconnected,
422ddcc4b4bSInès Varhol      * and it can be set low externally when in open-drain mode.
423ddcc4b4bSInès Varhol      */
424*27c335a4SCédric Le Goater     unsigned int pin = test_pin(data);
425*27c335a4SCédric Le Goater     uint32_t gpio = test_gpio_addr(data);
426ddcc4b4bSInès Varhol     uint32_t gpio2 = GPIO_BASE_ADDR + (GPIO_H - gpio);
427ddcc4b4bSInès Varhol 
428ddcc4b4bSInès Varhol     qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg");
429ddcc4b4bSInès Varhol 
430ddcc4b4bSInès Varhol     /* Setting a line high externally, configuring it in open-drain output */
431ddcc4b4bSInès Varhol     /* And checking the pin was disconnected */
432ddcc4b4bSInès Varhol     gpio_set_irq(gpio, pin, 1);
433ddcc4b4bSInès Varhol     gpio_set_bit(gpio, OTYPER, pin, OTYPER_OPEN_DRAIN);
434ddcc4b4bSInès Varhol     gpio_set_2bits(gpio, MODER, pin, MODER_OUTPUT);
435ddcc4b4bSInès Varhol     g_assert_cmphex(get_disconnected_pins(gpio), ==, 0xFFFF);
436ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
437ddcc4b4bSInès Varhol 
438ddcc4b4bSInès Varhol     /* Setting a line low externally, configuring it in open-drain output */
439ddcc4b4bSInès Varhol     /* And checking the pin wasn't disconnected */
440ddcc4b4bSInès Varhol     gpio_set_irq(gpio2, pin, 0);
441ddcc4b4bSInès Varhol     gpio_set_bit(gpio2, ODR, pin, 1);
442ddcc4b4bSInès Varhol     gpio_set_bit(gpio2, OTYPER, pin, OTYPER_OPEN_DRAIN);
443ddcc4b4bSInès Varhol     gpio_set_2bits(gpio2, MODER, pin, MODER_OUTPUT);
444ddcc4b4bSInès Varhol     g_assert_cmphex(get_disconnected_pins(gpio2), ==, 0xFFFF & ~(1 << pin));
445ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio2, IDR), ==,
446ddcc4b4bSInès Varhol                                reset(gpio2, IDR) & ~(1 << pin));
447ddcc4b4bSInès Varhol 
448ddcc4b4bSInès Varhol     /* Trying to set a open-drain output pin, checking it doesn't work */
449ddcc4b4bSInès Varhol     gpio_set_irq(gpio, pin, 1);
450ddcc4b4bSInès Varhol     g_assert_cmphex(get_disconnected_pins(gpio), ==, 0xFFFF);
451ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin));
452ddcc4b4bSInès Varhol 
453ddcc4b4bSInès Varhol     /* Trying to reset a open-drain output pin, checking it works */
454ddcc4b4bSInès Varhol     gpio_set_bit(gpio, ODR, pin, 1);
455ddcc4b4bSInès Varhol     gpio_set_irq(gpio, pin, 0);
456ddcc4b4bSInès Varhol     g_assert_cmphex(get_disconnected_pins(gpio2), ==, 0xFFFF & ~(1 << pin));
457ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio2, IDR), ==,
458ddcc4b4bSInès Varhol                                reset(gpio2, IDR) & ~(1 << pin));
459ddcc4b4bSInès Varhol 
460ddcc4b4bSInès Varhol     /* Clean the test */
461ddcc4b4bSInès Varhol     disconnect_all_pins(gpio2);
462ddcc4b4bSInès Varhol     gpio_writel(gpio2, OTYPER, reset(gpio2, OTYPER));
463ddcc4b4bSInès Varhol     gpio_writel(gpio2, ODR, reset(gpio2, ODR));
464ddcc4b4bSInès Varhol     gpio_writel(gpio2, MODER, reset(gpio2, MODER));
465ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio2, IDR), ==, reset(gpio2, IDR));
466ddcc4b4bSInès Varhol     disconnect_all_pins(gpio);
467ddcc4b4bSInès Varhol     gpio_writel(gpio, OTYPER, reset(gpio, OTYPER));
468ddcc4b4bSInès Varhol     gpio_writel(gpio, ODR, reset(gpio, ODR));
469ddcc4b4bSInès Varhol     gpio_writel(gpio, MODER, reset(gpio, MODER));
470ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR));
471ddcc4b4bSInès Varhol }
472ddcc4b4bSInès Varhol 
473ddcc4b4bSInès Varhol static void test_bsrr_brr(const void *data)
474ddcc4b4bSInès Varhol {
475ddcc4b4bSInès Varhol     /*
476ddcc4b4bSInès Varhol      * Test that writing a '1' in BSS and BSRR
477ddcc4b4bSInès Varhol      * has the desired effect on ODR.
478ddcc4b4bSInès Varhol      * In BSRR, BSx has priority over BRx.
479ddcc4b4bSInès Varhol      */
480*27c335a4SCédric Le Goater     unsigned int pin = test_pin(data);
481*27c335a4SCédric Le Goater     uint32_t gpio = test_gpio_addr(data);
482ddcc4b4bSInès Varhol 
483ddcc4b4bSInès Varhol     gpio_writel(gpio, BSRR, (1 << pin));
484ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR) | (1 << pin));
485ddcc4b4bSInès Varhol 
486ddcc4b4bSInès Varhol     gpio_writel(gpio, BSRR, (1 << (pin + NUM_GPIO_PINS)));
487ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR));
488ddcc4b4bSInès Varhol 
489ddcc4b4bSInès Varhol     gpio_writel(gpio, BSRR, (1 << pin));
490ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR) | (1 << pin));
491ddcc4b4bSInès Varhol 
492ddcc4b4bSInès Varhol     gpio_writel(gpio, BRR, (1 << pin));
493ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR));
494ddcc4b4bSInès Varhol 
495ddcc4b4bSInès Varhol     /* BSx should have priority over BRx */
496ddcc4b4bSInès Varhol     gpio_writel(gpio, BSRR, (1 << pin) | (1 << (pin + NUM_GPIO_PINS)));
497ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR) | (1 << pin));
498ddcc4b4bSInès Varhol 
499ddcc4b4bSInès Varhol     gpio_writel(gpio, BRR, (1 << pin));
500ddcc4b4bSInès Varhol     g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR));
501ddcc4b4bSInès Varhol 
502ddcc4b4bSInès Varhol     gpio_writel(gpio, ODR, reset(gpio, ODR));
503ddcc4b4bSInès Varhol }
504ddcc4b4bSInès Varhol 
505ddcc4b4bSInès Varhol int main(int argc, char **argv)
506ddcc4b4bSInès Varhol {
507ddcc4b4bSInès Varhol     int ret;
508ddcc4b4bSInès Varhol 
509ddcc4b4bSInès Varhol     g_test_init(&argc, &argv, NULL);
510ddcc4b4bSInès Varhol     g_test_set_nonfatal_assertions();
511ddcc4b4bSInès Varhol     qtest_add_func("stm32l4x5/gpio/test_idr_reset_value",
512ddcc4b4bSInès Varhol                    test_idr_reset_value);
513ddcc4b4bSInès Varhol     /*
514ddcc4b4bSInès Varhol      * The inputs for the tests (gpio and pin) can be changed,
515ddcc4b4bSInès Varhol      * but the tests don't work for pins that are high at reset
516ddcc4b4bSInès Varhol      * (GPIOA15, GPIO13 and GPIOB5).
517ddcc4b4bSInès Varhol      * Specifically, rising the pin then checking `get_irq()`
518ddcc4b4bSInès Varhol      * is problematic since the pin was already high.
519ddcc4b4bSInès Varhol      */
520ddcc4b4bSInès Varhol     qtest_add_data_func("stm32l4x5/gpio/test_gpioc5_output_mode",
521*27c335a4SCédric Le Goater                         test_data(GPIO_C, 5),
522ddcc4b4bSInès Varhol                         test_gpio_output_mode);
523ddcc4b4bSInès Varhol     qtest_add_data_func("stm32l4x5/gpio/test_gpioh3_output_mode",
524*27c335a4SCédric Le Goater                         test_data(GPIO_H, 3),
525ddcc4b4bSInès Varhol                         test_gpio_output_mode);
526ddcc4b4bSInès Varhol     qtest_add_data_func("stm32l4x5/gpio/test_gpio_input_mode1",
527*27c335a4SCédric Le Goater                         test_data(GPIO_D, 6),
528ddcc4b4bSInès Varhol                         test_gpio_input_mode);
529ddcc4b4bSInès Varhol     qtest_add_data_func("stm32l4x5/gpio/test_gpio_input_mode2",
530*27c335a4SCédric Le Goater                         test_data(GPIO_C, 10),
531ddcc4b4bSInès Varhol                         test_gpio_input_mode);
532ddcc4b4bSInès Varhol     qtest_add_data_func("stm32l4x5/gpio/test_gpio_pull_up_pull_down1",
533*27c335a4SCédric Le Goater                         test_data(GPIO_B, 5),
534ddcc4b4bSInès Varhol                         test_pull_up_pull_down);
535ddcc4b4bSInès Varhol     qtest_add_data_func("stm32l4x5/gpio/test_gpio_pull_up_pull_down2",
536*27c335a4SCédric Le Goater                         test_data(GPIO_F, 1),
537ddcc4b4bSInès Varhol                         test_pull_up_pull_down);
538ddcc4b4bSInès Varhol     qtest_add_data_func("stm32l4x5/gpio/test_gpio_push_pull1",
539*27c335a4SCédric Le Goater                         test_data(GPIO_G, 6),
540ddcc4b4bSInès Varhol                         test_push_pull);
541ddcc4b4bSInès Varhol     qtest_add_data_func("stm32l4x5/gpio/test_gpio_push_pull2",
542*27c335a4SCédric Le Goater                         test_data(GPIO_H, 3),
543ddcc4b4bSInès Varhol                         test_push_pull);
544ddcc4b4bSInès Varhol     qtest_add_data_func("stm32l4x5/gpio/test_gpio_open_drain1",
545*27c335a4SCédric Le Goater                         test_data(GPIO_C, 4),
546ddcc4b4bSInès Varhol                         test_open_drain);
547ddcc4b4bSInès Varhol     qtest_add_data_func("stm32l4x5/gpio/test_gpio_open_drain2",
548*27c335a4SCédric Le Goater                         test_data(GPIO_E, 11),
549ddcc4b4bSInès Varhol                         test_open_drain);
550ddcc4b4bSInès Varhol     qtest_add_data_func("stm32l4x5/gpio/test_bsrr_brr1",
551*27c335a4SCédric Le Goater                         test_data(GPIO_A, 12),
552ddcc4b4bSInès Varhol                         test_bsrr_brr);
553ddcc4b4bSInès Varhol     qtest_add_data_func("stm32l4x5/gpio/test_bsrr_brr2",
554*27c335a4SCédric Le Goater                         test_data(GPIO_D, 0),
555ddcc4b4bSInès Varhol                         test_bsrr_brr);
556ddcc4b4bSInès Varhol 
557ddcc4b4bSInès Varhol     qtest_start("-machine b-l475e-iot01a");
558ddcc4b4bSInès Varhol     ret = g_test_run();
559ddcc4b4bSInès Varhol     qtest_end();
560ddcc4b4bSInès Varhol 
561ddcc4b4bSInès Varhol     return ret;
562ddcc4b4bSInès Varhol }
563