]>
Commit | Line | Data |
---|---|---|
73314f13 HW |
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) | |
999be4a2 | 48 | #define APB4CKDIV(rv) extract32((rv), 30, 2) |
73314f13 HW |
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 | ||
999be4a2 HW |
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 | ||
73314f13 HW |
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 | ||
30258545 | 204 | g_assert(diff >= 0 && diff < ARRAY_SIZE(pwm_module_list)); |
73314f13 HW |
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 | ||
30258545 | 214 | g_assert(diff >= 0 && diff < ARRAY_SIZE(pwm_list)); |
73314f13 HW |
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; | |
3e829c04 | 222 | uint64_t val; |
73314f13 HW |
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")); | |
3e829c04 GQ |
230 | val = qnum_get_uint(qobject_to(QNum, qdict_get(response, "return"))); |
231 | qobject_unref(response); | |
232 | return val; | |
73314f13 HW |
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 | ||
999be4a2 HW |
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 | ||
73314f13 HW |
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 | ||
999be4a2 | 279 | static uint64_t read_pclk(QTestState *qts, bool mft) |
73314f13 HW |
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); | |
999be4a2 | 286 | uint32_t apbdiv = mft ? APB4CKDIV(clkdiv2) : APB3CKDIV(clkdiv2); |
73314f13 HW |
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 | ||
999be4a2 | 305 | freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + apbdiv); |
73314f13 HW |
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 | { | |
999be4a2 | 331 | return read_pclk(qts, false) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1)); |
73314f13 HW |
332 | } |
333 | ||
334 | static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted) | |
335 | { | |
1e5ce6e1 | 336 | uint32_t duty; |
73314f13 HW |
337 | |
338 | if (cnr == 0) { | |
339 | /* PWM is stopped. */ | |
340 | duty = 0; | |
341 | } else if (cmr >= cnr) { | |
342 | duty = MAX_DUTY; | |
343 | } else { | |
1e5ce6e1 | 344 | duty = (uint64_t)MAX_DUTY * (cmr + 1) / (cnr + 1); |
73314f13 HW |
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 | ||
999be4a2 HW |
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 | ||
73314f13 HW |
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 | ||
999be4a2 HW |
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 | ||
73314f13 HW |
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; | |
999be4a2 | 546 | QTestState *qts = qtest_init("-machine npcm750-evb"); |
73314f13 HW |
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; | |
999be4a2 | 560 | QTestState *qts = qtest_init("-machine npcm750-evb"); |
73314f13 HW |
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; | |
999be4a2 | 591 | QTestState *qts = qtest_init("-machine npcm750-evb"); |
73314f13 HW |
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 | ||
999be4a2 HW |
598 | mft_init(qts, td); |
599 | ||
73314f13 HW |
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 | ||
999be4a2 HW |
633 | /* Test MFT's RPM is correct. */ |
634 | mft_verify_rpm(qts, td, expected_duty); | |
635 | ||
73314f13 HW |
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 | } |