Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20210330' into...
[qemu/ar7.git] / tests / qtest / npcm7xx_pwm-test.c
blobbd15a1c294be25d978a91ef9592d8e185e68e97f
1 /*
2 * QTests for Nuvoton NPCM7xx PWM Modules.
4 * Copyright 2020 Google LLC
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.
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.
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"
23 #define REF_HZ 25000000
25 /* Register field definitions. */
26 #define CH_EN BIT(0)
27 #define CH_INV BIT(2)
28 #define CH_MOD BIT(3)
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
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)
54 #define MAX_DUTY 1000000
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
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
80 #define MFT_CKC_C2CSEL BIT(3)
81 #define MFT_CKC_C1CSEL BIT(0)
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)
90 #define MFT_MAX_CNT 0xffff
91 #define MFT_TIMEOUT 0x5000
93 #define DEFAULT_RPM 19800
94 #define DEFAULT_PRSC 255
95 #define MFT_PULSE_PER_REVOLUTION 2
97 #define MAX_ERROR 1
99 typedef struct PWMModule {
100 int irq;
101 uint64_t base_addr;
102 } PWMModule;
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;
111 typedef struct TestData {
112 const PWMModule *module;
113 const PWM *pwm;
114 } TestData;
116 static const PWMModule pwm_module_list[] = {
118 .irq = 93,
119 .base_addr = 0xf0103000
122 .irq = 94,
123 .base_addr = 0xf0104000
127 static const PWM pwm_list[] = {
129 .cnr_offset = 0x0c,
130 .cmr_offset = 0x10,
131 .pdr_offset = 0x14,
132 .pwdr_offset = 0x44,
135 .cnr_offset = 0x18,
136 .cmr_offset = 0x1c,
137 .pdr_offset = 0x20,
138 .pwdr_offset = 0x48,
141 .cnr_offset = 0x24,
142 .cmr_offset = 0x28,
143 .pdr_offset = 0x2c,
144 .pwdr_offset = 0x4c,
147 .cnr_offset = 0x30,
148 .cmr_offset = 0x34,
149 .pdr_offset = 0x38,
150 .pwdr_offset = 0x50,
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 };
158 static const uint32_t ppr_list[] = {
162 100,
163 255, /* Max possible value. */
166 static const uint32_t csr_list[] = {
171 4, /* Max possible value. */
174 static const uint32_t cnr_list[] = {
178 100,
179 150,
180 200,
181 1000,
182 10000,
183 65535, /* Max possible value. */
186 static const uint32_t cmr_list[] = {
191 100,
192 150,
193 200,
194 1000,
195 10000,
196 65535, /* Max possible value. */
199 /* Returns the index of the PWM module. */
200 static int pwm_module_index(const PWMModule *module)
202 ptrdiff_t diff = module - pwm_module_list;
204 g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_module_list));
206 return diff;
209 /* Returns the index of the PWM entry. */
210 static int pwm_index(const PWM *pwm)
212 ptrdiff_t diff = pwm - pwm_list;
214 g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_list));
216 return diff;
219 static uint64_t pwm_qom_get(QTestState *qts, const char *path, const char *name)
221 QDict *response;
222 uint64_t val;
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;
235 static uint64_t pwm_get_freq(QTestState *qts, int module_index, int pwm_index)
237 char path[100];
238 char name[100];
240 sprintf(path, "/machine/soc/pwm[%d]", module_index);
241 sprintf(name, "freq[%d]", pwm_index);
243 return pwm_qom_get(qts, path, name);
246 static uint64_t pwm_get_duty(QTestState *qts, int module_index, int pwm_index)
248 char path[100];
249 char name[100];
251 sprintf(path, "/machine/soc/pwm[%d]", module_index);
252 sprintf(name, "duty[%d]", pwm_index);
254 return pwm_qom_get(qts, path, name);
257 static void mft_qom_set(QTestState *qts, int index, const char *name,
258 uint32_t value)
260 QDict *response;
261 char *path = g_strdup_printf("/machine/soc/mft[%d]", index);
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"));
273 static uint32_t get_pll(uint32_t con)
275 return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con)
276 * PLL_OTDV2(con));
279 static uint64_t read_pclk(QTestState *qts, bool mft)
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);
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();
305 freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + apbdiv);
307 return freq;
310 static uint32_t pwm_selector(uint32_t csr)
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();
328 static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr,
329 uint32_t cnr)
331 return read_pclk(qts, false) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1));
334 static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted)
336 uint32_t duty;
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);
347 if (inverted) {
348 duty = MAX_DUTY - duty;
351 return duty;
354 static uint32_t pwm_read(QTestState *qts, const TestData *td, unsigned offset)
356 return qtest_readl(qts, td->module->base_addr + offset);
359 static void pwm_write(QTestState *qts, const TestData *td, unsigned offset,
360 uint32_t value)
362 qtest_writel(qts, td->module->base_addr + offset, value);
365 static uint8_t mft_readb(QTestState *qts, int index, unsigned offset)
367 return qtest_readb(qts, MFT_BA(index) + offset);
370 static uint16_t mft_readw(QTestState *qts, int index, unsigned offset)
372 return qtest_readw(qts, MFT_BA(index) + offset);
375 static void mft_writeb(QTestState *qts, int index, unsigned offset,
376 uint8_t value)
378 qtest_writeb(qts, MFT_BA(index) + offset, value);
381 static void mft_writew(QTestState *qts, int index, unsigned offset,
382 uint16_t value)
384 return qtest_writew(qts, MFT_BA(index) + offset, value);
387 static uint32_t pwm_read_ppr(QTestState *qts, const TestData *td)
389 return extract32(pwm_read(qts, td, PPR), ppr_base[pwm_index(td->pwm)], 8);
392 static void pwm_write_ppr(QTestState *qts, const TestData *td, uint32_t value)
394 pwm_write(qts, td, PPR, value << ppr_base[pwm_index(td->pwm)]);
397 static uint32_t pwm_read_csr(QTestState *qts, const TestData *td)
399 return extract32(pwm_read(qts, td, CSR), csr_base[pwm_index(td->pwm)], 3);
402 static void pwm_write_csr(QTestState *qts, const TestData *td, uint32_t value)
404 pwm_write(qts, td, CSR, value << csr_base[pwm_index(td->pwm)]);
407 static uint32_t pwm_read_pcr(QTestState *qts, const TestData *td)
409 return extract32(pwm_read(qts, td, PCR), pcr_base[pwm_index(td->pwm)], 4);
412 static void pwm_write_pcr(QTestState *qts, const TestData *td, uint32_t value)
414 pwm_write(qts, td, PCR, value << pcr_base[pwm_index(td->pwm)]);
417 static uint32_t pwm_read_cnr(QTestState *qts, const TestData *td)
419 return pwm_read(qts, td, td->pwm->cnr_offset);
422 static void pwm_write_cnr(QTestState *qts, const TestData *td, uint32_t value)
424 pwm_write(qts, td, td->pwm->cnr_offset, value);
427 static uint32_t pwm_read_cmr(QTestState *qts, const TestData *td)
429 return pwm_read(qts, td, td->pwm->cmr_offset);
432 static void pwm_write_cmr(QTestState *qts, const TestData *td, uint32_t value)
434 pwm_write(qts, td, td->pwm->cmr_offset, value);
437 static int mft_compute_index(const TestData *td)
439 int index = pwm_module_index(td->module) * ARRAY_SIZE(pwm_list) +
440 pwm_index(td->pwm);
442 g_assert_cmpint(index, <,
443 ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list));
445 return index;
448 static void mft_reset_counters(QTestState *qts, int index)
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);
458 static void mft_init(QTestState *qts, const TestData *td)
460 int index = mft_compute_index(td);
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);
470 /* Set cpcfg to use EQ mode, same as kernel driver */
471 mft_writeb(qts, index, MFT_CPCFG, MFT_CPCFG_EQ_MODE);
473 /* Write default counters, timeout and prescaler */
474 mft_reset_counters(qts, index);
475 mft_writeb(qts, index, MFT_PRSC, DEFAULT_PRSC);
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);
482 static int32_t mft_compute_cnt(uint32_t rpm, uint64_t clk)
484 uint64_t cnt;
486 if (rpm == 0) {
487 return -1;
490 cnt = clk * 60 / ((DEFAULT_PRSC + 1) * rpm * MFT_PULSE_PER_REVOLUTION);
491 if (cnt >= MFT_TIMEOUT) {
492 return -1;
494 return MFT_MAX_CNT - cnt;
497 static void mft_verify_rpm(QTestState *qts, const TestData *td, uint64_t duty)
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);
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);
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);
528 * Due to error in clock measurement and rounding, we might have a small
529 * error in measuring RPM.
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);
537 /* Verify rpm for fan B */
539 qtest_irq_intercept_out(qts, "/machine/soc/a9mpcore/gic");
542 /* Check pwm registers can be reset to default value */
543 static void test_init(gconstpointer test_data)
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);
550 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0);
551 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0);
553 qtest_quit(qts);
556 /* One-shot mode should not change frequency and duty cycle. */
557 static void test_oneshot(gconstpointer test_data)
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;
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);
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);
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);
584 qtest_quit(qts);
587 /* In toggle mode, the PWM generates correct outputs. */
588 static void test_toggle(gconstpointer test_data)
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;
598 mft_init(qts, td);
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);
605 for (j = 0; j < ARRAY_SIZE(csr_list); ++j) {
606 csr = csr_list[j];
607 pwm_write_csr(qts, td, csr);
609 for (k = 0; k < ARRAY_SIZE(cnr_list); ++k) {
610 cnr = cnr_list[k];
611 pwm_write_cnr(qts, td, cnr);
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);
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);
633 /* Test MFT's RPM is correct. */
634 mft_verify_rpm(qts, td, expected_duty);
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);
653 qtest_quit(qts);
656 static void pwm_add_test(const char *name, const TestData* td,
657 GTestDataFunc fn)
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);
664 #define add_test(name, td) pwm_add_test(#name, td, test_##name)
666 int main(int argc, char **argv)
668 TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)];
670 g_test_init(&argc, &argv, NULL);
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];
676 td->module = &pwm_module_list[i];
677 td->pwm = &pwm_list[j];
679 add_test(init, td);
680 add_test(oneshot, td);
681 add_test(toggle, td);
685 return g_test_run();