npcm7xx_gpio-test.c (14364B)
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 56static 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. */ 65static 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 80static 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 94static 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 113static 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 137static 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 156static 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 193static 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 205static 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 236static 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 258static 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 284static 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 310static 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 354int 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}