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