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 "libqos/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 273 static uint32_t get_pll(uint32_t con) 274 { 275 return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con) 276 * PLL_OTDV2(con)); 277 } 278 279 static uint64_t read_pclk(QTestState *qts, bool mft) 280 { 281 uint64_t freq = REF_HZ; 282 uint32_t clksel = qtest_readl(qts, CLK_BA + CLKSEL); 283 uint32_t pllcon; 284 uint32_t clkdiv1 = qtest_readl(qts, CLK_BA + CLKDIV1); 285 uint32_t clkdiv2 = qtest_readl(qts, CLK_BA + CLKDIV2); 286 uint32_t apbdiv = mft ? APB4CKDIV(clkdiv2) : APB3CKDIV(clkdiv2); 287 288 switch (CPUCKSEL(clksel)) { 289 case 0: 290 pllcon = qtest_readl(qts, CLK_BA + PLLCON0); 291 freq = get_pll(pllcon); 292 break; 293 case 1: 294 pllcon = qtest_readl(qts, CLK_BA + PLLCON1); 295 freq = get_pll(pllcon); 296 break; 297 case 2: 298 break; 299 case 3: 300 break; 301 default: 302 g_assert_not_reached(); 303 } 304 305 freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + apbdiv); 306 307 return freq; 308 } 309 310 static uint32_t pwm_selector(uint32_t csr) 311 { 312 switch (csr) { 313 case 0: 314 return 2; 315 case 1: 316 return 4; 317 case 2: 318 return 8; 319 case 3: 320 return 16; 321 case 4: 322 return 1; 323 default: 324 g_assert_not_reached(); 325 } 326 } 327 328 static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr, 329 uint32_t cnr) 330 { 331 return read_pclk(qts, false) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1)); 332 } 333 334 static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted) 335 { 336 uint32_t duty; 337 338 if (cnr == 0) { 339 /* PWM is stopped. */ 340 duty = 0; 341 } else if (cmr >= cnr) { 342 duty = MAX_DUTY; 343 } else { 344 duty = (uint64_t)MAX_DUTY * (cmr + 1) / (cnr + 1); 345 } 346 347 if (inverted) { 348 duty = MAX_DUTY - duty; 349 } 350 351 return duty; 352 } 353 354 static uint32_t pwm_read(QTestState *qts, const TestData *td, unsigned offset) 355 { 356 return qtest_readl(qts, td->module->base_addr + offset); 357 } 358 359 static void pwm_write(QTestState *qts, const TestData *td, unsigned offset, 360 uint32_t value) 361 { 362 qtest_writel(qts, td->module->base_addr + offset, value); 363 } 364 365 static uint8_t mft_readb(QTestState *qts, int index, unsigned offset) 366 { 367 return qtest_readb(qts, MFT_BA(index) + offset); 368 } 369 370 static uint16_t mft_readw(QTestState *qts, int index, unsigned offset) 371 { 372 return qtest_readw(qts, MFT_BA(index) + offset); 373 } 374 375 static void mft_writeb(QTestState *qts, int index, unsigned offset, 376 uint8_t value) 377 { 378 qtest_writeb(qts, MFT_BA(index) + offset, value); 379 } 380 381 static void mft_writew(QTestState *qts, int index, unsigned offset, 382 uint16_t value) 383 { 384 return qtest_writew(qts, MFT_BA(index) + offset, value); 385 } 386 387 static uint32_t pwm_read_ppr(QTestState *qts, const TestData *td) 388 { 389 return extract32(pwm_read(qts, td, PPR), ppr_base[pwm_index(td->pwm)], 8); 390 } 391 392 static void pwm_write_ppr(QTestState *qts, const TestData *td, uint32_t value) 393 { 394 pwm_write(qts, td, PPR, value << ppr_base[pwm_index(td->pwm)]); 395 } 396 397 static uint32_t pwm_read_csr(QTestState *qts, const TestData *td) 398 { 399 return extract32(pwm_read(qts, td, CSR), csr_base[pwm_index(td->pwm)], 3); 400 } 401 402 static void pwm_write_csr(QTestState *qts, const TestData *td, uint32_t value) 403 { 404 pwm_write(qts, td, CSR, value << csr_base[pwm_index(td->pwm)]); 405 } 406 407 static uint32_t pwm_read_pcr(QTestState *qts, const TestData *td) 408 { 409 return extract32(pwm_read(qts, td, PCR), pcr_base[pwm_index(td->pwm)], 4); 410 } 411 412 static void pwm_write_pcr(QTestState *qts, const TestData *td, uint32_t value) 413 { 414 pwm_write(qts, td, PCR, value << pcr_base[pwm_index(td->pwm)]); 415 } 416 417 static uint32_t pwm_read_cnr(QTestState *qts, const TestData *td) 418 { 419 return pwm_read(qts, td, td->pwm->cnr_offset); 420 } 421 422 static void pwm_write_cnr(QTestState *qts, const TestData *td, uint32_t value) 423 { 424 pwm_write(qts, td, td->pwm->cnr_offset, value); 425 } 426 427 static uint32_t pwm_read_cmr(QTestState *qts, const TestData *td) 428 { 429 return pwm_read(qts, td, td->pwm->cmr_offset); 430 } 431 432 static void pwm_write_cmr(QTestState *qts, const TestData *td, uint32_t value) 433 { 434 pwm_write(qts, td, td->pwm->cmr_offset, value); 435 } 436 437 static int mft_compute_index(const TestData *td) 438 { 439 int index = pwm_module_index(td->module) * ARRAY_SIZE(pwm_list) + 440 pwm_index(td->pwm); 441 442 g_assert_cmpint(index, <, 443 ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)); 444 445 return index; 446 } 447 448 static void mft_reset_counters(QTestState *qts, int index) 449 { 450 mft_writew(qts, index, MFT_CNT1, MFT_MAX_CNT); 451 mft_writew(qts, index, MFT_CNT2, MFT_MAX_CNT); 452 mft_writew(qts, index, MFT_CRA, MFT_MAX_CNT); 453 mft_writew(qts, index, MFT_CRB, MFT_MAX_CNT); 454 mft_writew(qts, index, MFT_CPA, MFT_MAX_CNT - MFT_TIMEOUT); 455 mft_writew(qts, index, MFT_CPB, MFT_MAX_CNT - MFT_TIMEOUT); 456 } 457 458 static void mft_init(QTestState *qts, const TestData *td) 459 { 460 int index = mft_compute_index(td); 461 462 /* Enable everything */ 463 mft_writeb(qts, index, MFT_CKC, 0); 464 mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL); 465 mft_writeb(qts, index, MFT_MCTRL, MFT_MCTRL_ALL); 466 mft_writeb(qts, index, MFT_IEN, MFT_IEN_ALL); 467 mft_writeb(qts, index, MFT_INASEL, 0); 468 mft_writeb(qts, index, MFT_INBSEL, 0); 469 470 /* Set cpcfg to use EQ mode, same as kernel driver */ 471 mft_writeb(qts, index, MFT_CPCFG, MFT_CPCFG_EQ_MODE); 472 473 /* Write default counters, timeout and prescaler */ 474 mft_reset_counters(qts, index); 475 mft_writeb(qts, index, MFT_PRSC, DEFAULT_PRSC); 476 477 /* Write default max rpm via QMP */ 478 mft_qom_set(qts, index, "max_rpm[0]", DEFAULT_RPM); 479 mft_qom_set(qts, index, "max_rpm[1]", DEFAULT_RPM); 480 } 481 482 static int32_t mft_compute_cnt(uint32_t rpm, uint64_t clk) 483 { 484 uint64_t cnt; 485 486 if (rpm == 0) { 487 return -1; 488 } 489 490 cnt = clk * 60 / ((DEFAULT_PRSC + 1) * rpm * MFT_PULSE_PER_REVOLUTION); 491 if (cnt >= MFT_TIMEOUT) { 492 return -1; 493 } 494 return MFT_MAX_CNT - cnt; 495 } 496 497 static void mft_verify_rpm(QTestState *qts, const TestData *td, uint64_t duty) 498 { 499 int index = mft_compute_index(td); 500 uint16_t cnt, cr; 501 uint32_t rpm = DEFAULT_RPM * duty / MAX_DUTY; 502 uint64_t clk = read_pclk(qts, true); 503 int32_t expected_cnt = mft_compute_cnt(rpm, clk); 504 505 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 506 g_test_message( 507 "verifying rpm for mft[%d]: clk: %" PRIu64 ", duty: %" PRIu64 ", rpm: %u, cnt: %d", 508 index, clk, duty, rpm, expected_cnt); 509 510 /* Verify rpm for fan A */ 511 /* Stop capture */ 512 mft_writeb(qts, index, MFT_CKC, 0); 513 mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL); 514 mft_reset_counters(qts, index); 515 g_assert_cmphex(mft_readw(qts, index, MFT_CNT1), ==, MFT_MAX_CNT); 516 g_assert_cmphex(mft_readw(qts, index, MFT_CRA), ==, MFT_MAX_CNT); 517 g_assert_cmphex(mft_readw(qts, index, MFT_CPA), ==, 518 MFT_MAX_CNT - MFT_TIMEOUT); 519 /* Start capture */ 520 mft_writeb(qts, index, MFT_CKC, MFT_CKC_C1CSEL); 521 g_assert_true(qtest_get_irq(qts, MFT_IRQ(index))); 522 if (expected_cnt == -1) { 523 g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TEPND); 524 } else { 525 g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TAPND); 526 cnt = mft_readw(qts, index, MFT_CNT1); 527 /* 528 * Due to error in clock measurement and rounding, we might have a small 529 * error in measuring RPM. 530 */ 531 g_assert_cmphex(cnt + MAX_ERROR, >=, expected_cnt); 532 g_assert_cmphex(cnt, <=, expected_cnt + MAX_ERROR); 533 cr = mft_readw(qts, index, MFT_CRA); 534 g_assert_cmphex(cnt, ==, cr); 535 } 536 537 /* Verify rpm for fan B */ 538 539 qtest_irq_intercept_out(qts, "/machine/soc/a9mpcore/gic"); 540 } 541 542 /* Check pwm registers can be reset to default value */ 543 static void test_init(gconstpointer test_data) 544 { 545 const TestData *td = test_data; 546 QTestState *qts = qtest_init("-machine npcm750-evb"); 547 int module = pwm_module_index(td->module); 548 int pwm = pwm_index(td->pwm); 549 550 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0); 551 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0); 552 553 qtest_quit(qts); 554 } 555 556 /* One-shot mode should not change frequency and duty cycle. */ 557 static void test_oneshot(gconstpointer test_data) 558 { 559 const TestData *td = test_data; 560 QTestState *qts = qtest_init("-machine npcm750-evb"); 561 int module = pwm_module_index(td->module); 562 int pwm = pwm_index(td->pwm); 563 uint32_t ppr, csr, pcr; 564 int i, j; 565 566 pcr = CH_EN; 567 for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) { 568 ppr = ppr_list[i]; 569 pwm_write_ppr(qts, td, ppr); 570 571 for (j = 0; j < ARRAY_SIZE(csr_list); ++j) { 572 csr = csr_list[j]; 573 pwm_write_csr(qts, td, csr); 574 pwm_write_pcr(qts, td, pcr); 575 576 g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr); 577 g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr); 578 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr); 579 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0); 580 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0); 581 } 582 } 583 584 qtest_quit(qts); 585 } 586 587 /* In toggle mode, the PWM generates correct outputs. */ 588 static void test_toggle(gconstpointer test_data) 589 { 590 const TestData *td = test_data; 591 QTestState *qts = qtest_init("-machine npcm750-evb"); 592 int module = pwm_module_index(td->module); 593 int pwm = pwm_index(td->pwm); 594 uint32_t ppr, csr, pcr, cnr, cmr; 595 int i, j, k, l; 596 uint64_t expected_freq, expected_duty; 597 598 mft_init(qts, td); 599 600 pcr = CH_EN | CH_MOD; 601 for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) { 602 ppr = ppr_list[i]; 603 pwm_write_ppr(qts, td, ppr); 604 605 for (j = 0; j < ARRAY_SIZE(csr_list); ++j) { 606 csr = csr_list[j]; 607 pwm_write_csr(qts, td, csr); 608 609 for (k = 0; k < ARRAY_SIZE(cnr_list); ++k) { 610 cnr = cnr_list[k]; 611 pwm_write_cnr(qts, td, cnr); 612 613 for (l = 0; l < ARRAY_SIZE(cmr_list); ++l) { 614 cmr = cmr_list[l]; 615 pwm_write_cmr(qts, td, cmr); 616 expected_freq = pwm_compute_freq(qts, ppr, csr, cnr); 617 expected_duty = pwm_compute_duty(cnr, cmr, false); 618 619 pwm_write_pcr(qts, td, pcr); 620 g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr); 621 g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr); 622 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr); 623 g_assert_cmpuint(pwm_read_cnr(qts, td), ==, cnr); 624 g_assert_cmpuint(pwm_read_cmr(qts, td), ==, cmr); 625 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), 626 ==, expected_duty); 627 if (expected_duty != 0 && expected_duty != 100) { 628 /* Duty cycle with 0 or 100 doesn't need frequency. */ 629 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), 630 ==, expected_freq); 631 } 632 633 /* Test MFT's RPM is correct. */ 634 mft_verify_rpm(qts, td, expected_duty); 635 636 /* Test inverted mode */ 637 expected_duty = pwm_compute_duty(cnr, cmr, true); 638 pwm_write_pcr(qts, td, pcr | CH_INV); 639 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr | CH_INV); 640 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), 641 ==, expected_duty); 642 if (expected_duty != 0 && expected_duty != 100) { 643 /* Duty cycle with 0 or 100 doesn't need frequency. */ 644 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), 645 ==, expected_freq); 646 } 647 648 } 649 } 650 } 651 } 652 653 qtest_quit(qts); 654 } 655 656 static void pwm_add_test(const char *name, const TestData* td, 657 GTestDataFunc fn) 658 { 659 g_autofree char *full_name = g_strdup_printf( 660 "npcm7xx_pwm/module[%d]/pwm[%d]/%s", pwm_module_index(td->module), 661 pwm_index(td->pwm), name); 662 qtest_add_data_func(full_name, td, fn); 663 } 664 #define add_test(name, td) pwm_add_test(#name, td, test_##name) 665 666 int main(int argc, char **argv) 667 { 668 TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)]; 669 670 g_test_init(&argc, &argv, NULL); 671 672 for (int i = 0; i < ARRAY_SIZE(pwm_module_list); ++i) { 673 for (int j = 0; j < ARRAY_SIZE(pwm_list); ++j) { 674 TestData *td = &test_data_list[i * ARRAY_SIZE(pwm_list) + j]; 675 676 td->module = &pwm_module_list[i]; 677 td->pwm = &pwm_list[j]; 678 679 add_test(init, td); 680 add_test(oneshot, td); 681 add_test(toggle, td); 682 } 683 } 684 685 return g_test_run(); 686 } 687