]>
Commit | Line | Data |
---|---|---|
526dbbe0 HS |
1 | /* |
2 | * QTest testcase for the Nuvoton NPCM7xx GPIO modules. | |
3 | * | |
4 | * Copyright 2020 Google LLC | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
15 | */ | |
16 | ||
17 | #include "qemu/osdep.h" | |
18 | #include "libqtest-single.h" | |
19 | ||
20 | #define NR_GPIO_DEVICES (8) | |
21 | #define GPIO(x) (0xf0010000 + (x) * 0x1000) | |
22 | #define GPIO_IRQ(x) (116 + (x)) | |
23 | ||
24 | /* GPIO registers */ | |
25 | #define GP_N_TLOCK1 0x00 | |
26 | #define GP_N_DIN 0x04 /* Data IN */ | |
27 | #define GP_N_POL 0x08 /* Polarity */ | |
28 | #define GP_N_DOUT 0x0c /* Data OUT */ | |
29 | #define GP_N_OE 0x10 /* Output Enable */ | |
30 | #define GP_N_OTYP 0x14 | |
31 | #define GP_N_MP 0x18 | |
32 | #define GP_N_PU 0x1c /* Pull-up */ | |
33 | #define GP_N_PD 0x20 /* Pull-down */ | |
34 | #define GP_N_DBNC 0x24 /* Debounce */ | |
35 | #define GP_N_EVTYP 0x28 /* Event Type */ | |
36 | #define GP_N_EVBE 0x2c /* Event Both Edge */ | |
37 | #define GP_N_OBL0 0x30 | |
38 | #define GP_N_OBL1 0x34 | |
39 | #define GP_N_OBL2 0x38 | |
40 | #define GP_N_OBL3 0x3c | |
41 | #define GP_N_EVEN 0x40 /* Event Enable */ | |
42 | #define GP_N_EVENS 0x44 /* Event Set (enable) */ | |
43 | #define GP_N_EVENC 0x48 /* Event Clear (disable) */ | |
44 | #define GP_N_EVST 0x4c /* Event Status */ | |
45 | #define GP_N_SPLCK 0x50 | |
46 | #define GP_N_MPLCK 0x54 | |
47 | #define GP_N_IEM 0x58 /* Input Enable */ | |
48 | #define GP_N_OSRC 0x5c | |
49 | #define GP_N_ODSC 0x60 | |
50 | #define GP_N_DOS 0x68 /* Data OUT Set */ | |
51 | #define GP_N_DOC 0x6c /* Data OUT Clear */ | |
52 | #define GP_N_OES 0x70 /* Output Enable Set */ | |
53 | #define GP_N_OEC 0x74 /* Output Enable Clear */ | |
54 | #define GP_N_TLOCK2 0x7c | |
55 | ||
56 | static void gpio_unlock(int n) | |
57 | { | |
58 | if (readl(GPIO(n) + GP_N_TLOCK1) != 0) { | |
59 | writel(GPIO(n) + GP_N_TLOCK2, 0xc0de1248); | |
60 | writel(GPIO(n) + GP_N_TLOCK1, 0xc0defa73); | |
61 | } | |
62 | } | |
63 | ||
64 | /* Restore the GPIO controller to a sensible default state. */ | |
65 | static void gpio_reset(int n) | |
66 | { | |
67 | gpio_unlock(0); | |
68 | ||
69 | writel(GPIO(n) + GP_N_EVEN, 0x00000000); | |
70 | writel(GPIO(n) + GP_N_EVST, 0xffffffff); | |
71 | writel(GPIO(n) + GP_N_POL, 0x00000000); | |
72 | writel(GPIO(n) + GP_N_DOUT, 0x00000000); | |
73 | writel(GPIO(n) + GP_N_OE, 0x00000000); | |
74 | writel(GPIO(n) + GP_N_OTYP, 0x00000000); | |
75 | writel(GPIO(n) + GP_N_PU, 0xffffffff); | |
76 | writel(GPIO(n) + GP_N_PD, 0x00000000); | |
77 | writel(GPIO(n) + GP_N_IEM, 0xffffffff); | |
78 | } | |
79 | ||
80 | static void test_dout_to_din(void) | |
81 | { | |
82 | gpio_reset(0); | |
83 | ||
84 | /* When output is enabled, DOUT should be reflected on DIN. */ | |
85 | writel(GPIO(0) + GP_N_OE, 0xffffffff); | |
86 | /* PU and PD shouldn't have any impact on DIN. */ | |
87 | writel(GPIO(0) + GP_N_PU, 0xffff0000); | |
88 | writel(GPIO(0) + GP_N_PD, 0x0000ffff); | |
89 | writel(GPIO(0) + GP_N_DOUT, 0x12345678); | |
90 | g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0x12345678); | |
91 | g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x12345678); | |
92 | } | |
93 | ||
94 | static void test_pullup_pulldown(void) | |
95 | { | |
96 | gpio_reset(0); | |
97 | ||
98 | /* | |
99 | * When output is disabled, and PD is the inverse of PU, PU should be | |
100 | * reflected on DIN. If PD is not the inverse of PU, the state of DIN is | |
101 | * undefined, so we don't test that. | |
102 | */ | |
103 | writel(GPIO(0) + GP_N_OE, 0x00000000); | |
104 | /* DOUT shouldn't have any impact on DIN. */ | |
105 | writel(GPIO(0) + GP_N_DOUT, 0xffff0000); | |
106 | writel(GPIO(0) + GP_N_PU, 0x23456789); | |
107 | writel(GPIO(0) + GP_N_PD, ~0x23456789U); | |
108 | g_assert_cmphex(readl(GPIO(0) + GP_N_PU), ==, 0x23456789); | |
109 | g_assert_cmphex(readl(GPIO(0) + GP_N_PD), ==, ~0x23456789U); | |
110 | g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x23456789); | |
111 | } | |
112 | ||
113 | static void test_output_enable(void) | |
114 | { | |
115 | gpio_reset(0); | |
116 | ||
117 | /* | |
118 | * With all pins weakly pulled down, and DOUT all-ones, OE should be | |
119 | * reflected on DIN. | |
120 | */ | |
121 | writel(GPIO(0) + GP_N_DOUT, 0xffffffff); | |
122 | writel(GPIO(0) + GP_N_PU, 0x00000000); | |
123 | writel(GPIO(0) + GP_N_PD, 0xffffffff); | |
124 | writel(GPIO(0) + GP_N_OE, 0x3456789a); | |
125 | g_assert_cmphex(readl(GPIO(0) + GP_N_OE), ==, 0x3456789a); | |
126 | g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x3456789a); | |
127 | ||
128 | writel(GPIO(0) + GP_N_OEC, 0x00030002); | |
129 | g_assert_cmphex(readl(GPIO(0) + GP_N_OE), ==, 0x34547898); | |
130 | g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x34547898); | |
131 | ||
132 | writel(GPIO(0) + GP_N_OES, 0x0000f001); | |
133 | g_assert_cmphex(readl(GPIO(0) + GP_N_OE), ==, 0x3454f899); | |
134 | g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x3454f899); | |
135 | } | |
136 | ||
137 | static void test_open_drain(void) | |
138 | { | |
139 | gpio_reset(0); | |
140 | ||
141 | /* | |
142 | * Upper half of DOUT drives a 1 only if the corresponding bit in OTYP is | |
143 | * not set. If OTYP is set, DIN is determined by PU/PD. Lower half of | |
144 | * DOUT always drives a 0 regardless of OTYP; PU/PD have no effect. When | |
145 | * OE is 0, output is determined by PU/PD; OTYP has no effect. | |
146 | */ | |
147 | writel(GPIO(0) + GP_N_OTYP, 0x456789ab); | |
148 | writel(GPIO(0) + GP_N_OE, 0xf0f0f0f0); | |
149 | writel(GPIO(0) + GP_N_DOUT, 0xffff0000); | |
150 | writel(GPIO(0) + GP_N_PU, 0xff00ff00); | |
151 | writel(GPIO(0) + GP_N_PD, 0x00ff00ff); | |
152 | g_assert_cmphex(readl(GPIO(0) + GP_N_OTYP), ==, 0x456789ab); | |
153 | g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0xff900f00); | |
154 | } | |
155 | ||
156 | static void test_polarity(void) | |
157 | { | |
158 | gpio_reset(0); | |
159 | ||
160 | /* | |
161 | * In push-pull mode, DIN should reflect DOUT because the signal is | |
162 | * inverted in both directions. | |
163 | */ | |
164 | writel(GPIO(0) + GP_N_OTYP, 0x00000000); | |
165 | writel(GPIO(0) + GP_N_OE, 0xffffffff); | |
166 | writel(GPIO(0) + GP_N_DOUT, 0x56789abc); | |
167 | writel(GPIO(0) + GP_N_POL, 0x6789abcd); | |
168 | g_assert_cmphex(readl(GPIO(0) + GP_N_POL), ==, 0x6789abcd); | |
169 | g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x56789abc); | |
170 | ||
171 | /* | |
172 | * When turning off the drivers, DIN should reflect the inverse of the | |
173 | * pulled-up lines. | |
174 | */ | |
175 | writel(GPIO(0) + GP_N_OE, 0x00000000); | |
176 | writel(GPIO(0) + GP_N_POL, 0xffffffff); | |
177 | writel(GPIO(0) + GP_N_PU, 0x789abcde); | |
178 | writel(GPIO(0) + GP_N_PD, ~0x789abcdeU); | |
179 | g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, ~0x789abcdeU); | |
180 | ||
181 | /* | |
182 | * In open-drain mode, DOUT=1 will appear to drive the pin high (since DIN | |
183 | * is inverted), while DOUT=0 will leave the pin floating. | |
184 | */ | |
185 | writel(GPIO(0) + GP_N_OTYP, 0xffffffff); | |
186 | writel(GPIO(0) + GP_N_OE, 0xffffffff); | |
187 | writel(GPIO(0) + GP_N_PU, 0xffff0000); | |
188 | writel(GPIO(0) + GP_N_PD, 0x0000ffff); | |
189 | writel(GPIO(0) + GP_N_DOUT, 0xff00ff00); | |
190 | g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0xff00ffff); | |
191 | } | |
192 | ||
193 | static void test_input_mask(void) | |
194 | { | |
195 | gpio_reset(0); | |
196 | ||
197 | /* IEM=0 forces the input to zero before polarity inversion. */ | |
198 | writel(GPIO(0) + GP_N_OE, 0xffffffff); | |
199 | writel(GPIO(0) + GP_N_DOUT, 0xff00ff00); | |
200 | writel(GPIO(0) + GP_N_POL, 0xffff0000); | |
201 | writel(GPIO(0) + GP_N_IEM, 0x87654321); | |
202 | g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0xff9a4300); | |
203 | } | |
204 | ||
205 | static void test_temp_lock(void) | |
206 | { | |
207 | gpio_reset(0); | |
208 | ||
209 | writel(GPIO(0) + GP_N_DOUT, 0x98765432); | |
210 | ||
211 | /* Make sure we're unlocked initially. */ | |
212 | g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 0); | |
213 | /* Writing any value to TLOCK1 will lock. */ | |
214 | writel(GPIO(0) + GP_N_TLOCK1, 0); | |
215 | g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 1); | |
216 | writel(GPIO(0) + GP_N_DOUT, 0xa9876543); | |
217 | g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0x98765432); | |
218 | /* Now, try to unlock. */ | |
219 | gpio_unlock(0); | |
220 | g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 0); | |
221 | writel(GPIO(0) + GP_N_DOUT, 0xa9876543); | |
222 | g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0xa9876543); | |
223 | ||
224 | /* Try it again, but write TLOCK2 to lock. */ | |
225 | writel(GPIO(0) + GP_N_TLOCK2, 0); | |
226 | g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 1); | |
227 | writel(GPIO(0) + GP_N_DOUT, 0x98765432); | |
228 | g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0xa9876543); | |
229 | /* Now, try to unlock. */ | |
230 | gpio_unlock(0); | |
231 | g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 0); | |
232 | writel(GPIO(0) + GP_N_DOUT, 0x98765432); | |
233 | g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0x98765432); | |
234 | } | |
235 | ||
236 | static void test_events_level(void) | |
237 | { | |
238 | gpio_reset(0); | |
239 | ||
240 | writel(GPIO(0) + GP_N_EVTYP, 0x00000000); | |
241 | writel(GPIO(0) + GP_N_DOUT, 0xba987654); | |
242 | writel(GPIO(0) + GP_N_OE, 0xffffffff); | |
243 | writel(GPIO(0) + GP_N_EVST, 0xffffffff); | |
244 | ||
245 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0xba987654); | |
246 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
247 | writel(GPIO(0) + GP_N_DOUT, 0x00000000); | |
248 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0xba987654); | |
249 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
250 | writel(GPIO(0) + GP_N_EVST, 0x00007654); | |
251 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0xba980000); | |
252 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
253 | writel(GPIO(0) + GP_N_EVST, 0xba980000); | |
254 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000); | |
255 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
256 | } | |
257 | ||
258 | static void test_events_rising_edge(void) | |
259 | { | |
260 | gpio_reset(0); | |
261 | ||
262 | writel(GPIO(0) + GP_N_EVTYP, 0xffffffff); | |
263 | writel(GPIO(0) + GP_N_EVBE, 0x00000000); | |
264 | writel(GPIO(0) + GP_N_DOUT, 0xffff0000); | |
265 | writel(GPIO(0) + GP_N_OE, 0xffffffff); | |
266 | writel(GPIO(0) + GP_N_EVST, 0xffffffff); | |
267 | ||
268 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000); | |
269 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
270 | writel(GPIO(0) + GP_N_DOUT, 0xff00ff00); | |
271 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x0000ff00); | |
272 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
273 | writel(GPIO(0) + GP_N_DOUT, 0x00ff0000); | |
274 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00ffff00); | |
275 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
276 | writel(GPIO(0) + GP_N_EVST, 0x0000f000); | |
277 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00ff0f00); | |
278 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
279 | writel(GPIO(0) + GP_N_EVST, 0x00ff0f00); | |
280 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000); | |
281 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
282 | } | |
283 | ||
284 | static void test_events_both_edges(void) | |
285 | { | |
286 | gpio_reset(0); | |
287 | ||
288 | writel(GPIO(0) + GP_N_EVTYP, 0xffffffff); | |
289 | writel(GPIO(0) + GP_N_EVBE, 0xffffffff); | |
290 | writel(GPIO(0) + GP_N_DOUT, 0xffff0000); | |
291 | writel(GPIO(0) + GP_N_OE, 0xffffffff); | |
292 | writel(GPIO(0) + GP_N_EVST, 0xffffffff); | |
293 | ||
294 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000); | |
295 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
296 | writel(GPIO(0) + GP_N_DOUT, 0xff00ff00); | |
297 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00ffff00); | |
298 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
299 | writel(GPIO(0) + GP_N_DOUT, 0xef00ff08); | |
300 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x10ffff08); | |
301 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
302 | writel(GPIO(0) + GP_N_EVST, 0x0000f000); | |
303 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x10ff0f08); | |
304 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
305 | writel(GPIO(0) + GP_N_EVST, 0x10ff0f08); | |
306 | g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000); | |
307 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); | |
308 | } | |
309 | ||
310 | static void test_gpion_irq(gconstpointer test_data) | |
311 | { | |
312 | intptr_t n = (intptr_t)test_data; | |
313 | ||
314 | gpio_reset(n); | |
315 | ||
316 | writel(GPIO(n) + GP_N_EVTYP, 0x00000000); | |
317 | writel(GPIO(n) + GP_N_DOUT, 0x00000000); | |
318 | writel(GPIO(n) + GP_N_OE, 0xffffffff); | |
319 | writel(GPIO(n) + GP_N_EVST, 0xffffffff); | |
320 | writel(GPIO(n) + GP_N_EVEN, 0x00000000); | |
321 | ||
322 | /* Trigger an event; interrupts are masked. */ | |
323 | g_assert_cmphex(readl(GPIO(n) + GP_N_EVST), ==, 0x00000000); | |
324 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(n))); | |
325 | writel(GPIO(n) + GP_N_DOS, 0x00008000); | |
326 | g_assert_cmphex(readl(GPIO(n) + GP_N_EVST), ==, 0x00008000); | |
327 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(n))); | |
328 | ||
329 | /* Unmask all event interrupts; verify that the interrupt fired. */ | |
330 | writel(GPIO(n) + GP_N_EVEN, 0xffffffff); | |
331 | g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n))); | |
332 | ||
333 | /* Clear the current bit, set a new bit, irq stays asserted. */ | |
334 | writel(GPIO(n) + GP_N_DOC, 0x00008000); | |
335 | g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n))); | |
336 | writel(GPIO(n) + GP_N_DOS, 0x00000200); | |
337 | g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n))); | |
338 | writel(GPIO(n) + GP_N_EVST, 0x00008000); | |
339 | g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n))); | |
340 | ||
341 | /* Mask/unmask the event that's currently active. */ | |
342 | writel(GPIO(n) + GP_N_EVENC, 0x00000200); | |
343 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(n))); | |
344 | writel(GPIO(n) + GP_N_EVENS, 0x00000200); | |
345 | g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n))); | |
346 | ||
347 | /* Clear the input and the status bit, irq is deasserted. */ | |
348 | writel(GPIO(n) + GP_N_DOC, 0x00000200); | |
349 | g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n))); | |
350 | writel(GPIO(n) + GP_N_EVST, 0x00000200); | |
351 | g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(n))); | |
352 | } | |
353 | ||
354 | int main(int argc, char **argv) | |
355 | { | |
356 | int ret; | |
357 | int i; | |
358 | ||
359 | g_test_init(&argc, &argv, NULL); | |
360 | g_test_set_nonfatal_assertions(); | |
361 | ||
362 | qtest_add_func("/npcm7xx_gpio/dout_to_din", test_dout_to_din); | |
363 | qtest_add_func("/npcm7xx_gpio/pullup_pulldown", test_pullup_pulldown); | |
364 | qtest_add_func("/npcm7xx_gpio/output_enable", test_output_enable); | |
365 | qtest_add_func("/npcm7xx_gpio/open_drain", test_open_drain); | |
366 | qtest_add_func("/npcm7xx_gpio/polarity", test_polarity); | |
367 | qtest_add_func("/npcm7xx_gpio/input_mask", test_input_mask); | |
368 | qtest_add_func("/npcm7xx_gpio/temp_lock", test_temp_lock); | |
369 | qtest_add_func("/npcm7xx_gpio/events/level", test_events_level); | |
370 | qtest_add_func("/npcm7xx_gpio/events/rising_edge", test_events_rising_edge); | |
371 | qtest_add_func("/npcm7xx_gpio/events/both_edges", test_events_both_edges); | |
372 | ||
373 | for (i = 0; i < NR_GPIO_DEVICES; i++) { | |
374 | g_autofree char *test_name = | |
375 | g_strdup_printf("/npcm7xx_gpio/gpio[%d]/irq", i); | |
376 | qtest_add_data_func(test_name, (void *)(intptr_t)i, test_gpion_irq); | |
377 | } | |
378 | ||
379 | qtest_start("-machine npcm750-evb"); | |
380 | qtest_irq_intercept_in(global_qtest, "/machine/soc/a9mpcore/gic"); | |
381 | ret = g_test_run(); | |
382 | qtest_end(); | |
383 | ||
384 | return ret; | |
385 | } |