1 /* 2 * QTests for Nuvoton NPCM7xx PWM 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 "qemu/bitops.h" 19 #include "libqtest.h" 20 #include "qapi/qmp/qdict.h" 21 #include "qapi/qmp/qnum.h" 22 23 #define REF_HZ 25000000 24 25 /* Register field definitions. */ 26 #define CH_EN BIT(0) 27 #define CH_INV BIT(2) 28 #define CH_MOD BIT(3) 29 30 /* Registers shared between all PWMs in a module */ 31 #define PPR 0x00 32 #define CSR 0x04 33 #define PCR 0x08 34 #define PIER 0x3c 35 #define PIIR 0x40 36 37 /* CLK module related */ 38 #define CLK_BA 0xf0801000 39 #define CLKSEL 0x04 40 #define CLKDIV1 0x08 41 #define CLKDIV2 0x2c 42 #define PLLCON0 0x0c 43 #define PLLCON1 0x10 44 #define PLL_INDV(rv) extract32((rv), 0, 6) 45 #define PLL_FBDV(rv) extract32((rv), 16, 12) 46 #define PLL_OTDV1(rv) extract32((rv), 8, 3) 47 #define PLL_OTDV2(rv) extract32((rv), 13, 3) 48 #define APB4CKDIV(rv) extract32((rv), 30, 2) 49 #define APB3CKDIV(rv) extract32((rv), 28, 2) 50 #define CLK2CKDIV(rv) extract32((rv), 0, 1) 51 #define CLK4CKDIV(rv) extract32((rv), 26, 2) 52 #define CPUCKSEL(rv) extract32((rv), 0, 2) 53 54 #define MAX_DUTY 1000000 55 56 /* MFT (PWM fan) related */ 57 #define MFT_BA(n) (0xf0180000 + ((n) * 0x1000)) 58 #define MFT_IRQ(n) (96 + (n)) 59 #define MFT_CNT1 0x00 60 #define MFT_CRA 0x02 61 #define MFT_CRB 0x04 62 #define MFT_CNT2 0x06 63 #define MFT_PRSC 0x08 64 #define MFT_CKC 0x0a 65 #define MFT_MCTRL 0x0c 66 #define MFT_ICTRL 0x0e 67 #define MFT_ICLR 0x10 68 #define MFT_IEN 0x12 69 #define MFT_CPA 0x14 70 #define MFT_CPB 0x16 71 #define MFT_CPCFG 0x18 72 #define MFT_INASEL 0x1a 73 #define MFT_INBSEL 0x1c 74 75 #define MFT_MCTRL_ALL 0x64 76 #define MFT_ICLR_ALL 0x3f 77 #define MFT_IEN_ALL 0x3f 78 #define MFT_CPCFG_EQ_MODE 0x44 79 80 #define MFT_CKC_C2CSEL BIT(3) 81 #define MFT_CKC_C1CSEL BIT(0) 82 83 #define MFT_ICTRL_TFPND BIT(5) 84 #define MFT_ICTRL_TEPND BIT(4) 85 #define MFT_ICTRL_TDPND BIT(3) 86 #define MFT_ICTRL_TCPND BIT(2) 87 #define MFT_ICTRL_TBPND BIT(1) 88 #define MFT_ICTRL_TAPND BIT(0) 89 90 #define MFT_MAX_CNT 0xffff 91 #define MFT_TIMEOUT 0x5000 92 93 #define DEFAULT_RPM 19800 94 #define DEFAULT_PRSC 255 95 #define MFT_PULSE_PER_REVOLUTION 2 96 97 #define MAX_ERROR 1 98 99 typedef struct PWMModule { 100 int irq; 101 uint64_t base_addr; 102 } PWMModule; 103 104 typedef struct PWM { 105 uint32_t cnr_offset; 106 uint32_t cmr_offset; 107 uint32_t pdr_offset; 108 uint32_t pwdr_offset; 109 } PWM; 110 111 typedef struct TestData { 112 const PWMModule *module; 113 const PWM *pwm; 114 } TestData; 115 116 static const PWMModule pwm_module_list[] = { 117 { 118 .irq = 93, 119 .base_addr = 0xf0103000 120 }, 121 { 122 .irq = 94, 123 .base_addr = 0xf0104000 124 } 125 }; 126 127 static const PWM pwm_list[] = { 128 { 129 .cnr_offset = 0x0c, 130 .cmr_offset = 0x10, 131 .pdr_offset = 0x14, 132 .pwdr_offset = 0x44, 133 }, 134 { 135 .cnr_offset = 0x18, 136 .cmr_offset = 0x1c, 137 .pdr_offset = 0x20, 138 .pwdr_offset = 0x48, 139 }, 140 { 141 .cnr_offset = 0x24, 142 .cmr_offset = 0x28, 143 .pdr_offset = 0x2c, 144 .pwdr_offset = 0x4c, 145 }, 146 { 147 .cnr_offset = 0x30, 148 .cmr_offset = 0x34, 149 .pdr_offset = 0x38, 150 .pwdr_offset = 0x50, 151 }, 152 }; 153 154 static const int ppr_base[] = { 0, 0, 8, 8 }; 155 static const int csr_base[] = { 0, 4, 8, 12 }; 156 static const int pcr_base[] = { 0, 8, 12, 16 }; 157 158 static const uint32_t ppr_list[] = { 159 0, 160 1, 161 10, 162 100, 163 255, /* Max possible value. */ 164 }; 165 166 static const uint32_t csr_list[] = { 167 0, 168 1, 169 2, 170 3, 171 4, /* Max possible value. */ 172 }; 173 174 static const uint32_t cnr_list[] = { 175 0, 176 1, 177 50, 178 100, 179 150, 180 200, 181 1000, 182 10000, 183 65535, /* Max possible value. */ 184 }; 185 186 static const uint32_t cmr_list[] = { 187 0, 188 1, 189 10, 190 50, 191 100, 192 150, 193 200, 194 1000, 195 10000, 196 65535, /* Max possible value. */ 197 }; 198 199 /* Returns the index of the PWM module. */ 200 static int pwm_module_index(const PWMModule *module) 201 { 202 ptrdiff_t diff = module - pwm_module_list; 203 204 g_assert(diff >= 0 && diff < ARRAY_SIZE(pwm_module_list)); 205 206 return diff; 207 } 208 209 /* Returns the index of the PWM entry. */ 210 static int pwm_index(const PWM *pwm) 211 { 212 ptrdiff_t diff = pwm - pwm_list; 213 214 g_assert(diff >= 0 && diff < ARRAY_SIZE(pwm_list)); 215 216 return diff; 217 } 218 219 static uint64_t pwm_qom_get(QTestState *qts, const char *path, const char *name) 220 { 221 QDict *response; 222 uint64_t val; 223 224 g_test_message("Getting properties %s from %s", name, path); 225 response = qtest_qmp(qts, "{ 'execute': 'qom-get'," 226 " 'arguments': { 'path': %s, 'property': %s}}", 227 path, name); 228 /* The qom set message returns successfully. */ 229 g_assert_true(qdict_haskey(response, "return")); 230 val = qnum_get_uint(qobject_to(QNum, qdict_get(response, "return"))); 231 qobject_unref(response); 232 return val; 233 } 234 235 static uint64_t pwm_get_freq(QTestState *qts, int module_index, int pwm_index) 236 { 237 char path[100]; 238 char name[100]; 239 240 sprintf(path, "/machine/soc/pwm[%d]", module_index); 241 sprintf(name, "freq[%d]", pwm_index); 242 243 return pwm_qom_get(qts, path, name); 244 } 245 246 static uint64_t pwm_get_duty(QTestState *qts, int module_index, int pwm_index) 247 { 248 char path[100]; 249 char name[100]; 250 251 sprintf(path, "/machine/soc/pwm[%d]", module_index); 252 sprintf(name, "duty[%d]", pwm_index); 253 254 return pwm_qom_get(qts, path, name); 255 } 256 257 static void mft_qom_set(QTestState *qts, int index, const char *name, 258 uint32_t value) 259 { 260 QDict *response; 261 char *path = g_strdup_printf("/machine/soc/mft[%d]", index); 262 263 g_test_message("Setting properties %s of mft[%d] with value %u", 264 name, index, value); 265 response = qtest_qmp(qts, "{ 'execute': 'qom-set'," 266 " 'arguments': { 'path': %s, " 267 " 'property': %s, 'value': %u}}", 268 path, name, value); 269 /* The qom set message returns successfully. */ 270 g_assert_true(qdict_haskey(response, "return")); 271 272 qobject_unref(response); 273 g_free(path); 274 } 275 276 static uint32_t get_pll(uint32_t con) 277 { 278 return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con) 279 * PLL_OTDV2(con)); 280 } 281 282 static uint64_t read_pclk(QTestState *qts, bool mft) 283 { 284 uint64_t freq = REF_HZ; 285 uint32_t clksel = qtest_readl(qts, CLK_BA + CLKSEL); 286 uint32_t pllcon; 287 uint32_t clkdiv1 = qtest_readl(qts, CLK_BA + CLKDIV1); 288 uint32_t clkdiv2 = qtest_readl(qts, CLK_BA + CLKDIV2); 289 uint32_t apbdiv = mft ? APB4CKDIV(clkdiv2) : APB3CKDIV(clkdiv2); 290 291 switch (CPUCKSEL(clksel)) { 292 case 0: 293 pllcon = qtest_readl(qts, CLK_BA + PLLCON0); 294 freq = get_pll(pllcon); 295 break; 296 case 1: 297 pllcon = qtest_readl(qts, CLK_BA + PLLCON1); 298 freq = get_pll(pllcon); 299 break; 300 case 2: 301 break; 302 case 3: 303 break; 304 default: 305 g_assert_not_reached(); 306 } 307 308 freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + apbdiv); 309 310 return freq; 311 } 312 313 static uint32_t pwm_selector(uint32_t csr) 314 { 315 switch (csr) { 316 case 0: 317 return 2; 318 case 1: 319 return 4; 320 case 2: 321 return 8; 322 case 3: 323 return 16; 324 case 4: 325 return 1; 326 default: 327 g_assert_not_reached(); 328 } 329 } 330 331 static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr, 332 uint32_t cnr) 333 { 334 return read_pclk(qts, false) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1)); 335 } 336 337 static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted) 338 { 339 uint32_t duty; 340 341 if (cnr == 0) { 342 /* PWM is stopped. */ 343 duty = 0; 344 } else if (cmr >= cnr) { 345 duty = MAX_DUTY; 346 } else { 347 duty = (uint64_t)MAX_DUTY * (cmr + 1) / (cnr + 1); 348 } 349 350 if (inverted) { 351 duty = MAX_DUTY - duty; 352 } 353 354 return duty; 355 } 356 357 static uint32_t pwm_read(QTestState *qts, const TestData *td, unsigned offset) 358 { 359 return qtest_readl(qts, td->module->base_addr + offset); 360 } 361 362 static void pwm_write(QTestState *qts, const TestData *td, unsigned offset, 363 uint32_t value) 364 { 365 qtest_writel(qts, td->module->base_addr + offset, value); 366 } 367 368 static uint8_t mft_readb(QTestState *qts, int index, unsigned offset) 369 { 370 return qtest_readb(qts, MFT_BA(index) + offset); 371 } 372 373 static uint16_t mft_readw(QTestState *qts, int index, unsigned offset) 374 { 375 return qtest_readw(qts, MFT_BA(index) + offset); 376 } 377 378 static void mft_writeb(QTestState *qts, int index, unsigned offset, 379 uint8_t value) 380 { 381 qtest_writeb(qts, MFT_BA(index) + offset, value); 382 } 383 384 static void mft_writew(QTestState *qts, int index, unsigned offset, 385 uint16_t value) 386 { 387 return qtest_writew(qts, MFT_BA(index) + offset, value); 388 } 389 390 static uint32_t pwm_read_ppr(QTestState *qts, const TestData *td) 391 { 392 return extract32(pwm_read(qts, td, PPR), ppr_base[pwm_index(td->pwm)], 8); 393 } 394 395 static void pwm_write_ppr(QTestState *qts, const TestData *td, uint32_t value) 396 { 397 pwm_write(qts, td, PPR, value << ppr_base[pwm_index(td->pwm)]); 398 } 399 400 static uint32_t pwm_read_csr(QTestState *qts, const TestData *td) 401 { 402 return extract32(pwm_read(qts, td, CSR), csr_base[pwm_index(td->pwm)], 3); 403 } 404 405 static void pwm_write_csr(QTestState *qts, const TestData *td, uint32_t value) 406 { 407 pwm_write(qts, td, CSR, value << csr_base[pwm_index(td->pwm)]); 408 } 409 410 static uint32_t pwm_read_pcr(QTestState *qts, const TestData *td) 411 { 412 return extract32(pwm_read(qts, td, PCR), pcr_base[pwm_index(td->pwm)], 4); 413 } 414 415 static void pwm_write_pcr(QTestState *qts, const TestData *td, uint32_t value) 416 { 417 pwm_write(qts, td, PCR, value << pcr_base[pwm_index(td->pwm)]); 418 } 419 420 static uint32_t pwm_read_cnr(QTestState *qts, const TestData *td) 421 { 422 return pwm_read(qts, td, td->pwm->cnr_offset); 423 } 424 425 static void pwm_write_cnr(QTestState *qts, const TestData *td, uint32_t value) 426 { 427 pwm_write(qts, td, td->pwm->cnr_offset, value); 428 } 429 430 static uint32_t pwm_read_cmr(QTestState *qts, const TestData *td) 431 { 432 return pwm_read(qts, td, td->pwm->cmr_offset); 433 } 434 435 static void pwm_write_cmr(QTestState *qts, const TestData *td, uint32_t value) 436 { 437 pwm_write(qts, td, td->pwm->cmr_offset, value); 438 } 439 440 static int mft_compute_index(const TestData *td) 441 { 442 int index = pwm_module_index(td->module) * ARRAY_SIZE(pwm_list) + 443 pwm_index(td->pwm); 444 445 g_assert_cmpint(index, <, 446 ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)); 447 448 return index; 449 } 450 451 static void mft_reset_counters(QTestState *qts, int index) 452 { 453 mft_writew(qts, index, MFT_CNT1, MFT_MAX_CNT); 454 mft_writew(qts, index, MFT_CNT2, MFT_MAX_CNT); 455 mft_writew(qts, index, MFT_CRA, MFT_MAX_CNT); 456 mft_writew(qts, index, MFT_CRB, MFT_MAX_CNT); 457 mft_writew(qts, index, MFT_CPA, MFT_MAX_CNT - MFT_TIMEOUT); 458 mft_writew(qts, index, MFT_CPB, MFT_MAX_CNT - MFT_TIMEOUT); 459 } 460 461 static void mft_init(QTestState *qts, const TestData *td) 462 { 463 int index = mft_compute_index(td); 464 465 /* Enable everything */ 466 mft_writeb(qts, index, MFT_CKC, 0); 467 mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL); 468 mft_writeb(qts, index, MFT_MCTRL, MFT_MCTRL_ALL); 469 mft_writeb(qts, index, MFT_IEN, MFT_IEN_ALL); 470 mft_writeb(qts, index, MFT_INASEL, 0); 471 mft_writeb(qts, index, MFT_INBSEL, 0); 472 473 /* Set cpcfg to use EQ mode, same as kernel driver */ 474 mft_writeb(qts, index, MFT_CPCFG, MFT_CPCFG_EQ_MODE); 475 476 /* Write default counters, timeout and prescaler */ 477 mft_reset_counters(qts, index); 478 mft_writeb(qts, index, MFT_PRSC, DEFAULT_PRSC); 479 480 /* Write default max rpm via QMP */ 481 mft_qom_set(qts, index, "max_rpm[0]", DEFAULT_RPM); 482 mft_qom_set(qts, index, "max_rpm[1]", DEFAULT_RPM); 483 } 484 485 static int32_t mft_compute_cnt(uint32_t rpm, uint64_t clk) 486 { 487 uint64_t cnt; 488 489 if (rpm == 0) { 490 return -1; 491 } 492 493 cnt = clk * 60 / ((DEFAULT_PRSC + 1) * rpm * MFT_PULSE_PER_REVOLUTION); 494 if (cnt >= MFT_TIMEOUT) { 495 return -1; 496 } 497 return MFT_MAX_CNT - cnt; 498 } 499 500 static void mft_verify_rpm(QTestState *qts, const TestData *td, uint64_t duty) 501 { 502 int index = mft_compute_index(td); 503 uint16_t cnt, cr; 504 uint32_t rpm = DEFAULT_RPM * duty / MAX_DUTY; 505 uint64_t clk = read_pclk(qts, true); 506 int32_t expected_cnt = mft_compute_cnt(rpm, clk); 507 508 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 509 g_test_message( 510 "verifying rpm for mft[%d]: clk: %" PRIu64 ", duty: %" PRIu64 ", rpm: %u, cnt: %d", 511 index, clk, duty, rpm, expected_cnt); 512 513 /* Verify rpm for fan A */ 514 /* Stop capture */ 515 mft_writeb(qts, index, MFT_CKC, 0); 516 mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL); 517 mft_reset_counters(qts, index); 518 g_assert_cmphex(mft_readw(qts, index, MFT_CNT1), ==, MFT_MAX_CNT); 519 g_assert_cmphex(mft_readw(qts, index, MFT_CRA), ==, MFT_MAX_CNT); 520 g_assert_cmphex(mft_readw(qts, index, MFT_CPA), ==, 521 MFT_MAX_CNT - MFT_TIMEOUT); 522 /* Start capture */ 523 mft_writeb(qts, index, MFT_CKC, MFT_CKC_C1CSEL); 524 g_assert_true(qtest_get_irq(qts, MFT_IRQ(index))); 525 if (expected_cnt == -1) { 526 g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TEPND); 527 } else { 528 g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TAPND); 529 cnt = mft_readw(qts, index, MFT_CNT1); 530 /* 531 * Due to error in clock measurement and rounding, we might have a small 532 * error in measuring RPM. 533 */ 534 g_assert_cmphex(cnt + MAX_ERROR, >=, expected_cnt); 535 g_assert_cmphex(cnt, <=, expected_cnt + MAX_ERROR); 536 cr = mft_readw(qts, index, MFT_CRA); 537 g_assert_cmphex(cnt, ==, cr); 538 } 539 540 /* Verify rpm for fan B */ 541 542 qtest_irq_intercept_out(qts, "/machine/soc/a9mpcore/gic"); 543 } 544 545 /* Check pwm registers can be reset to default value */ 546 static void test_init(gconstpointer test_data) 547 { 548 const TestData *td = test_data; 549 QTestState *qts = qtest_init("-machine npcm750-evb"); 550 int module = pwm_module_index(td->module); 551 int pwm = pwm_index(td->pwm); 552 553 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0); 554 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0); 555 556 qtest_quit(qts); 557 } 558 559 /* One-shot mode should not change frequency and duty cycle. */ 560 static void test_oneshot(gconstpointer test_data) 561 { 562 const TestData *td = test_data; 563 QTestState *qts = qtest_init("-machine npcm750-evb"); 564 int module = pwm_module_index(td->module); 565 int pwm = pwm_index(td->pwm); 566 uint32_t ppr, csr, pcr; 567 int i, j; 568 569 pcr = CH_EN; 570 for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) { 571 ppr = ppr_list[i]; 572 pwm_write_ppr(qts, td, ppr); 573 574 for (j = 0; j < ARRAY_SIZE(csr_list); ++j) { 575 csr = csr_list[j]; 576 pwm_write_csr(qts, td, csr); 577 pwm_write_pcr(qts, td, pcr); 578 579 g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr); 580 g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr); 581 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr); 582 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0); 583 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0); 584 } 585 } 586 587 qtest_quit(qts); 588 } 589 590 /* In toggle mode, the PWM generates correct outputs. */ 591 static void test_toggle(gconstpointer test_data) 592 { 593 const TestData *td = test_data; 594 QTestState *qts = qtest_init("-machine npcm750-evb"); 595 int module = pwm_module_index(td->module); 596 int pwm = pwm_index(td->pwm); 597 uint32_t ppr, csr, pcr, cnr, cmr; 598 int i, j, k, l; 599 uint64_t expected_freq, expected_duty; 600 601 mft_init(qts, td); 602 603 pcr = CH_EN | CH_MOD; 604 for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) { 605 ppr = ppr_list[i]; 606 pwm_write_ppr(qts, td, ppr); 607 608 for (j = 0; j < ARRAY_SIZE(csr_list); ++j) { 609 csr = csr_list[j]; 610 pwm_write_csr(qts, td, csr); 611 612 for (k = 0; k < ARRAY_SIZE(cnr_list); ++k) { 613 cnr = cnr_list[k]; 614 pwm_write_cnr(qts, td, cnr); 615 616 for (l = 0; l < ARRAY_SIZE(cmr_list); ++l) { 617 cmr = cmr_list[l]; 618 pwm_write_cmr(qts, td, cmr); 619 expected_freq = pwm_compute_freq(qts, ppr, csr, cnr); 620 expected_duty = pwm_compute_duty(cnr, cmr, false); 621 622 pwm_write_pcr(qts, td, pcr); 623 g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr); 624 g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr); 625 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr); 626 g_assert_cmpuint(pwm_read_cnr(qts, td), ==, cnr); 627 g_assert_cmpuint(pwm_read_cmr(qts, td), ==, cmr); 628 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), 629 ==, expected_duty); 630 if (expected_duty != 0 && expected_duty != 100) { 631 /* Duty cycle with 0 or 100 doesn't need frequency. */ 632 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), 633 ==, expected_freq); 634 } 635 636 /* Test MFT's RPM is correct. */ 637 mft_verify_rpm(qts, td, expected_duty); 638 639 /* Test inverted mode */ 640 expected_duty = pwm_compute_duty(cnr, cmr, true); 641 pwm_write_pcr(qts, td, pcr | CH_INV); 642 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr | CH_INV); 643 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), 644 ==, expected_duty); 645 if (expected_duty != 0 && expected_duty != 100) { 646 /* Duty cycle with 0 or 100 doesn't need frequency. */ 647 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), 648 ==, expected_freq); 649 } 650 651 } 652 } 653 } 654 } 655 656 qtest_quit(qts); 657 } 658 659 static void pwm_add_test(const char *name, const TestData* td, 660 GTestDataFunc fn) 661 { 662 g_autofree char *full_name = g_strdup_printf( 663 "npcm7xx_pwm/module[%d]/pwm[%d]/%s", pwm_module_index(td->module), 664 pwm_index(td->pwm), name); 665 qtest_add_data_func(full_name, td, fn); 666 } 667 #define add_test(name, td) pwm_add_test(#name, td, test_##name) 668 669 int main(int argc, char **argv) 670 { 671 TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)]; 672 673 g_test_init(&argc, &argv, NULL); 674 675 for (int i = 0; i < ARRAY_SIZE(pwm_module_list); ++i) { 676 for (int j = 0; j < ARRAY_SIZE(pwm_list); ++j) { 677 TestData *td = &test_data_list[i * ARRAY_SIZE(pwm_list) + j]; 678 679 td->module = &pwm_module_list[i]; 680 td->pwm = &pwm_list[j]; 681 682 add_test(init, td); 683 add_test(oneshot, td); 684 add_test(toggle, td); 685 } 686 } 687 688 return g_test_run(); 689 } 690