npcm7xx_pwm-test.c (19500B)
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 99typedef struct PWMModule { 100 int irq; 101 uint64_t base_addr; 102} PWMModule; 103 104typedef 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 111typedef struct TestData { 112 const PWMModule *module; 113 const PWM *pwm; 114} TestData; 115 116static 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 127static 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 154static const int ppr_base[] = { 0, 0, 8, 8 }; 155static const int csr_base[] = { 0, 4, 8, 12 }; 156static const int pcr_base[] = { 0, 8, 12, 16 }; 157 158static const uint32_t ppr_list[] = { 159 0, 160 1, 161 10, 162 100, 163 255, /* Max possible value. */ 164}; 165 166static const uint32_t csr_list[] = { 167 0, 168 1, 169 2, 170 3, 171 4, /* Max possible value. */ 172}; 173 174static 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 186static 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. */ 200static 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. */ 210static 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 219static 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 235static 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 246static 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 257static 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 273static 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 279static 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 310static 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 328static 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 334static 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 354static uint32_t pwm_read(QTestState *qts, const TestData *td, unsigned offset) 355{ 356 return qtest_readl(qts, td->module->base_addr + offset); 357} 358 359static 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 365static uint8_t mft_readb(QTestState *qts, int index, unsigned offset) 366{ 367 return qtest_readb(qts, MFT_BA(index) + offset); 368} 369 370static uint16_t mft_readw(QTestState *qts, int index, unsigned offset) 371{ 372 return qtest_readw(qts, MFT_BA(index) + offset); 373} 374 375static 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 381static 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 387static 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 392static 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 397static 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 402static 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 407static 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 412static 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 417static uint32_t pwm_read_cnr(QTestState *qts, const TestData *td) 418{ 419 return pwm_read(qts, td, td->pwm->cnr_offset); 420} 421 422static 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 427static uint32_t pwm_read_cmr(QTestState *qts, const TestData *td) 428{ 429 return pwm_read(qts, td, td->pwm->cmr_offset); 430} 431 432static 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 437static 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 448static 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 458static 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 482static 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 497static 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 */ 543static 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. */ 557static 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. */ 588static 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 656static 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 666int 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}