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. */
pwm_module_index(const PWMModule * 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. */
pwm_index(const PWM * pwm)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
pwm_qom_get(QTestState * qts,const char * path,const char * name)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
pwm_get_freq(QTestState * qts,int module_index,int pwm_index)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
pwm_get_duty(QTestState * qts,int module_index,int pwm_index)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
mft_qom_set(QTestState * qts,int index,const char * name,uint32_t value)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
get_pll(uint32_t con)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
read_pclk(QTestState * qts,bool mft)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
pwm_selector(uint32_t csr)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
pwm_compute_freq(QTestState * qts,uint32_t ppr,uint32_t csr,uint32_t cnr)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
pwm_compute_duty(uint32_t cnr,uint32_t cmr,bool inverted)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
pwm_read(QTestState * qts,const TestData * td,unsigned offset)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
pwm_write(QTestState * qts,const TestData * td,unsigned offset,uint32_t value)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
mft_readb(QTestState * qts,int index,unsigned offset)374 static uint8_t mft_readb(QTestState *qts, int index, unsigned offset)
375 {
376 return qtest_readb(qts, MFT_BA(index) + offset);
377 }
378
mft_readw(QTestState * qts,int index,unsigned offset)379 static uint16_t mft_readw(QTestState *qts, int index, unsigned offset)
380 {
381 return qtest_readw(qts, MFT_BA(index) + offset);
382 }
383
mft_writeb(QTestState * qts,int index,unsigned offset,uint8_t value)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
mft_writew(QTestState * qts,int index,unsigned offset,uint16_t value)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
pwm_read_ppr(QTestState * qts,const TestData * td)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
pwm_write_ppr(QTestState * qts,const TestData * td,uint32_t value)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
pwm_read_csr(QTestState * qts,const TestData * td)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
pwm_write_csr(QTestState * qts,const TestData * td,uint32_t value)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
pwm_read_pcr(QTestState * qts,const TestData * td)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
pwm_write_pcr(QTestState * qts,const TestData * td,uint32_t value)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
pwm_read_cnr(QTestState * qts,const TestData * td)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
pwm_write_cnr(QTestState * qts,const TestData * td,uint32_t value)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
pwm_read_cmr(QTestState * qts,const TestData * td)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
pwm_write_cmr(QTestState * qts,const TestData * td,uint32_t value)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
mft_compute_index(const TestData * td)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
mft_reset_counters(QTestState * qts,int index)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
mft_init(QTestState * qts,const TestData * td)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
mft_compute_cnt(uint32_t rpm,uint64_t clk)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
mft_verify_rpm(QTestState * qts,const TestData * td,uint64_t duty)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 */
test_init(gconstpointer test_data)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. */
test_oneshot(gconstpointer test_data)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. */
test_toggle(gconstpointer test_data)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 int cnr_step = g_test_quick() ? 2 : 1;
610
611 mft_init(qts, td);
612
613 pcr = CH_EN | CH_MOD;
614 for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) {
615 ppr = ppr_list[i];
616 pwm_write_ppr(qts, td, ppr);
617
618 for (j = 0; j < ARRAY_SIZE(csr_list); ++j) {
619 csr = csr_list[j];
620 pwm_write_csr(qts, td, csr);
621
622 for (k = 0; k < ARRAY_SIZE(cnr_list); k += cnr_step) {
623 cnr = cnr_list[k];
624 pwm_write_cnr(qts, td, cnr);
625
626 for (l = 0; l < ARRAY_SIZE(cmr_list); ++l) {
627 cmr = cmr_list[l];
628 pwm_write_cmr(qts, td, cmr);
629 expected_freq = pwm_compute_freq(qts, ppr, csr, cnr);
630 expected_duty = pwm_compute_duty(cnr, cmr, false);
631
632 pwm_write_pcr(qts, td, pcr);
633 g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr);
634 g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr);
635 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr);
636 g_assert_cmpuint(pwm_read_cnr(qts, td), ==, cnr);
637 g_assert_cmpuint(pwm_read_cmr(qts, td), ==, cmr);
638 g_assert_cmpuint(pwm_get_duty(qts, module, pwm),
639 ==, expected_duty);
640 if (expected_duty != 0 && expected_duty != 100) {
641 /* Duty cycle with 0 or 100 doesn't need frequency. */
642 g_assert_cmpuint(pwm_get_freq(qts, module, pwm),
643 ==, expected_freq);
644 }
645
646 /* Test MFT's RPM is correct. */
647 mft_verify_rpm(qts, td, expected_duty);
648
649 /* Test inverted mode */
650 expected_duty = pwm_compute_duty(cnr, cmr, true);
651 pwm_write_pcr(qts, td, pcr | CH_INV);
652 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr | CH_INV);
653 g_assert_cmpuint(pwm_get_duty(qts, module, pwm),
654 ==, expected_duty);
655 if (expected_duty != 0 && expected_duty != 100) {
656 /* Duty cycle with 0 or 100 doesn't need frequency. */
657 g_assert_cmpuint(pwm_get_freq(qts, module, pwm),
658 ==, expected_freq);
659 }
660
661 }
662 }
663 }
664 }
665
666 qtest_quit(qts);
667 }
668
pwm_add_test(const char * name,const TestData * td,GTestDataFunc fn)669 static void pwm_add_test(const char *name, const TestData* td,
670 GTestDataFunc fn)
671 {
672 g_autofree char *full_name = g_strdup_printf(
673 "npcm7xx_pwm/module[%d]/pwm[%d]/%s", pwm_module_index(td->module),
674 pwm_index(td->pwm), name);
675 qtest_add_data_func(full_name, td, fn);
676 }
677 #define add_test(name, td) pwm_add_test(#name, td, test_##name)
678
main(int argc,char ** argv)679 int main(int argc, char **argv)
680 {
681 TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)];
682 int pwm_module_list_cnt = 1, pwm_list_cnt = 1;
683
684 char *v_env = getenv("V");
685
686 if (v_env) {
687 verbosity_level = atoi(v_env);
688 }
689
690 g_test_init(&argc, &argv, NULL);
691
692 if (!g_test_quick()) {
693 pwm_module_list_cnt = ARRAY_SIZE(pwm_module_list);
694 pwm_list_cnt = ARRAY_SIZE(pwm_list);
695 }
696
697 for (int i = 0; i < pwm_module_list_cnt; ++i) {
698 for (int j = 0; j < pwm_list_cnt; ++j) {
699 TestData *td = &test_data_list[i * ARRAY_SIZE(pwm_list) + j];
700
701 td->module = &pwm_module_list[i];
702 td->pwm = &pwm_list[j];
703
704 add_test(init, td);
705 add_test(oneshot, td);
706 add_test(toggle, td);
707 }
708 }
709
710 return g_test_run();
711 }
712