1
2
3
4
5
6
7
8
9
10
11
12
13
14
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#define REF_HZ 25000000
24
25
26#define CH_EN BIT(0)
27#define CH_INV BIT(2)
28#define CH_MOD BIT(3)
29
30
31#define PPR 0x00
32#define CSR 0x04
33#define PCR 0x08
34#define PIER 0x3c
35#define PIIR 0x40
36
37
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
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,
164};
165
166static const uint32_t csr_list[] = {
167 0,
168 1,
169 2,
170 3,
171 4,
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,
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,
197};
198
199
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
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
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
270 g_assert_true(qdict_haskey(response, "return"));
271
272 qobject_unref(response);
273 g_free(path);
274}
275
276static uint32_t get_pll(uint32_t con)
277{
278 return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con)
279 * PLL_OTDV2(con));
280}
281
282static uint64_t read_pclk(QTestState *qts, bool mft)
283{
284 uint64_t freq = REF_HZ;
285 uint32_t clksel = qtest_readl(qts, CLK_BA + CLKSEL);
286 uint32_t pllcon;
287 uint32_t clkdiv1 = qtest_readl(qts, CLK_BA + CLKDIV1);
288 uint32_t clkdiv2 = qtest_readl(qts, CLK_BA + CLKDIV2);
289 uint32_t apbdiv = mft ? APB4CKDIV(clkdiv2) : APB3CKDIV(clkdiv2);
290
291 switch (CPUCKSEL(clksel)) {
292 case 0:
293 pllcon = qtest_readl(qts, CLK_BA + PLLCON0);
294 freq = get_pll(pllcon);
295 break;
296 case 1:
297 pllcon = qtest_readl(qts, CLK_BA + PLLCON1);
298 freq = get_pll(pllcon);
299 break;
300 case 2:
301 break;
302 case 3:
303 break;
304 default:
305 g_assert_not_reached();
306 }
307
308 freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + apbdiv);
309
310 return freq;
311}
312
313static uint32_t pwm_selector(uint32_t csr)
314{
315 switch (csr) {
316 case 0:
317 return 2;
318 case 1:
319 return 4;
320 case 2:
321 return 8;
322 case 3:
323 return 16;
324 case 4:
325 return 1;
326 default:
327 g_assert_not_reached();
328 }
329}
330
331static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr,
332 uint32_t cnr)
333{
334 return read_pclk(qts, false) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1));
335}
336
337static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted)
338{
339 uint32_t duty;
340
341 if (cnr == 0) {
342
343 duty = 0;
344 } else if (cmr >= cnr) {
345 duty = MAX_DUTY;
346 } else {
347 duty = (uint64_t)MAX_DUTY * (cmr + 1) / (cnr + 1);
348 }
349
350 if (inverted) {
351 duty = MAX_DUTY - duty;
352 }
353
354 return duty;
355}
356
357static uint32_t pwm_read(QTestState *qts, const TestData *td, unsigned offset)
358{
359 return qtest_readl(qts, td->module->base_addr + offset);
360}
361
362static void pwm_write(QTestState *qts, const TestData *td, unsigned offset,
363 uint32_t value)
364{
365 qtest_writel(qts, td->module->base_addr + offset, value);
366}
367
368static uint8_t mft_readb(QTestState *qts, int index, unsigned offset)
369{
370 return qtest_readb(qts, MFT_BA(index) + offset);
371}
372
373static uint16_t mft_readw(QTestState *qts, int index, unsigned offset)
374{
375 return qtest_readw(qts, MFT_BA(index) + offset);
376}
377
378static void mft_writeb(QTestState *qts, int index, unsigned offset,
379 uint8_t value)
380{
381 qtest_writeb(qts, MFT_BA(index) + offset, value);
382}
383
384static void mft_writew(QTestState *qts, int index, unsigned offset,
385 uint16_t value)
386{
387 return qtest_writew(qts, MFT_BA(index) + offset, value);
388}
389
390static uint32_t pwm_read_ppr(QTestState *qts, const TestData *td)
391{
392 return extract32(pwm_read(qts, td, PPR), ppr_base[pwm_index(td->pwm)], 8);
393}
394
395static void pwm_write_ppr(QTestState *qts, const TestData *td, uint32_t value)
396{
397 pwm_write(qts, td, PPR, value << ppr_base[pwm_index(td->pwm)]);
398}
399
400static uint32_t pwm_read_csr(QTestState *qts, const TestData *td)
401{
402 return extract32(pwm_read(qts, td, CSR), csr_base[pwm_index(td->pwm)], 3);
403}
404
405static void pwm_write_csr(QTestState *qts, const TestData *td, uint32_t value)
406{
407 pwm_write(qts, td, CSR, value << csr_base[pwm_index(td->pwm)]);
408}
409
410static uint32_t pwm_read_pcr(QTestState *qts, const TestData *td)
411{
412 return extract32(pwm_read(qts, td, PCR), pcr_base[pwm_index(td->pwm)], 4);
413}
414
415static void pwm_write_pcr(QTestState *qts, const TestData *td, uint32_t value)
416{
417 pwm_write(qts, td, PCR, value << pcr_base[pwm_index(td->pwm)]);
418}
419
420static uint32_t pwm_read_cnr(QTestState *qts, const TestData *td)
421{
422 return pwm_read(qts, td, td->pwm->cnr_offset);
423}
424
425static void pwm_write_cnr(QTestState *qts, const TestData *td, uint32_t value)
426{
427 pwm_write(qts, td, td->pwm->cnr_offset, value);
428}
429
430static uint32_t pwm_read_cmr(QTestState *qts, const TestData *td)
431{
432 return pwm_read(qts, td, td->pwm->cmr_offset);
433}
434
435static void pwm_write_cmr(QTestState *qts, const TestData *td, uint32_t value)
436{
437 pwm_write(qts, td, td->pwm->cmr_offset, value);
438}
439
440static int mft_compute_index(const TestData *td)
441{
442 int index = pwm_module_index(td->module) * ARRAY_SIZE(pwm_list) +
443 pwm_index(td->pwm);
444
445 g_assert_cmpint(index, <,
446 ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list));
447
448 return index;
449}
450
451static void mft_reset_counters(QTestState *qts, int index)
452{
453 mft_writew(qts, index, MFT_CNT1, MFT_MAX_CNT);
454 mft_writew(qts, index, MFT_CNT2, MFT_MAX_CNT);
455 mft_writew(qts, index, MFT_CRA, MFT_MAX_CNT);
456 mft_writew(qts, index, MFT_CRB, MFT_MAX_CNT);
457 mft_writew(qts, index, MFT_CPA, MFT_MAX_CNT - MFT_TIMEOUT);
458 mft_writew(qts, index, MFT_CPB, MFT_MAX_CNT - MFT_TIMEOUT);
459}
460
461static void mft_init(QTestState *qts, const TestData *td)
462{
463 int index = mft_compute_index(td);
464
465
466 mft_writeb(qts, index, MFT_CKC, 0);
467 mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL);
468 mft_writeb(qts, index, MFT_MCTRL, MFT_MCTRL_ALL);
469 mft_writeb(qts, index, MFT_IEN, MFT_IEN_ALL);
470 mft_writeb(qts, index, MFT_INASEL, 0);
471 mft_writeb(qts, index, MFT_INBSEL, 0);
472
473
474 mft_writeb(qts, index, MFT_CPCFG, MFT_CPCFG_EQ_MODE);
475
476
477 mft_reset_counters(qts, index);
478 mft_writeb(qts, index, MFT_PRSC, DEFAULT_PRSC);
479
480
481 mft_qom_set(qts, index, "max_rpm[0]", DEFAULT_RPM);
482 mft_qom_set(qts, index, "max_rpm[1]", DEFAULT_RPM);
483}
484
485static int32_t mft_compute_cnt(uint32_t rpm, uint64_t clk)
486{
487 uint64_t cnt;
488
489 if (rpm == 0) {
490 return -1;
491 }
492
493 cnt = clk * 60 / ((DEFAULT_PRSC + 1) * rpm * MFT_PULSE_PER_REVOLUTION);
494 if (cnt >= MFT_TIMEOUT) {
495 return -1;
496 }
497 return MFT_MAX_CNT - cnt;
498}
499
500static void mft_verify_rpm(QTestState *qts, const TestData *td, uint64_t duty)
501{
502 int index = mft_compute_index(td);
503 uint16_t cnt, cr;
504 uint32_t rpm = DEFAULT_RPM * duty / MAX_DUTY;
505 uint64_t clk = read_pclk(qts, true);
506 int32_t expected_cnt = mft_compute_cnt(rpm, clk);
507
508 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
509 g_test_message(
510 "verifying rpm for mft[%d]: clk: %" PRIu64 ", duty: %" PRIu64 ", rpm: %u, cnt: %d",
511 index, clk, duty, rpm, expected_cnt);
512
513
514
515 mft_writeb(qts, index, MFT_CKC, 0);
516 mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL);
517 mft_reset_counters(qts, index);
518 g_assert_cmphex(mft_readw(qts, index, MFT_CNT1), ==, MFT_MAX_CNT);
519 g_assert_cmphex(mft_readw(qts, index, MFT_CRA), ==, MFT_MAX_CNT);
520 g_assert_cmphex(mft_readw(qts, index, MFT_CPA), ==,
521 MFT_MAX_CNT - MFT_TIMEOUT);
522
523 mft_writeb(qts, index, MFT_CKC, MFT_CKC_C1CSEL);
524 g_assert_true(qtest_get_irq(qts, MFT_IRQ(index)));
525 if (expected_cnt == -1) {
526 g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TEPND);
527 } else {
528 g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TAPND);
529 cnt = mft_readw(qts, index, MFT_CNT1);
530
531
532
533
534 g_assert_cmphex(cnt + MAX_ERROR, >=, expected_cnt);
535 g_assert_cmphex(cnt, <=, expected_cnt + MAX_ERROR);
536 cr = mft_readw(qts, index, MFT_CRA);
537 g_assert_cmphex(cnt, ==, cr);
538 }
539
540
541
542 qtest_irq_intercept_out(qts, "/machine/soc/a9mpcore/gic");
543}
544
545
546static void test_init(gconstpointer test_data)
547{
548 const TestData *td = test_data;
549 QTestState *qts = qtest_init("-machine npcm750-evb");
550 int module = pwm_module_index(td->module);
551 int pwm = pwm_index(td->pwm);
552
553 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0);
554 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0);
555
556 qtest_quit(qts);
557}
558
559
560static void test_oneshot(gconstpointer test_data)
561{
562 const TestData *td = test_data;
563 QTestState *qts = qtest_init("-machine npcm750-evb");
564 int module = pwm_module_index(td->module);
565 int pwm = pwm_index(td->pwm);
566 uint32_t ppr, csr, pcr;
567 int i, j;
568
569 pcr = CH_EN;
570 for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) {
571 ppr = ppr_list[i];
572 pwm_write_ppr(qts, td, ppr);
573
574 for (j = 0; j < ARRAY_SIZE(csr_list); ++j) {
575 csr = csr_list[j];
576 pwm_write_csr(qts, td, csr);
577 pwm_write_pcr(qts, td, pcr);
578
579 g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr);
580 g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr);
581 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr);
582 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0);
583 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0);
584 }
585 }
586
587 qtest_quit(qts);
588}
589
590
591static void test_toggle(gconstpointer test_data)
592{
593 const TestData *td = test_data;
594 QTestState *qts = qtest_init("-machine npcm750-evb");
595 int module = pwm_module_index(td->module);
596 int pwm = pwm_index(td->pwm);
597 uint32_t ppr, csr, pcr, cnr, cmr;
598 int i, j, k, l;
599 uint64_t expected_freq, expected_duty;
600
601 mft_init(qts, td);
602
603 pcr = CH_EN | CH_MOD;
604 for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) {
605 ppr = ppr_list[i];
606 pwm_write_ppr(qts, td, ppr);
607
608 for (j = 0; j < ARRAY_SIZE(csr_list); ++j) {
609 csr = csr_list[j];
610 pwm_write_csr(qts, td, csr);
611
612 for (k = 0; k < ARRAY_SIZE(cnr_list); ++k) {
613 cnr = cnr_list[k];
614 pwm_write_cnr(qts, td, cnr);
615
616 for (l = 0; l < ARRAY_SIZE(cmr_list); ++l) {
617 cmr = cmr_list[l];
618 pwm_write_cmr(qts, td, cmr);
619 expected_freq = pwm_compute_freq(qts, ppr, csr, cnr);
620 expected_duty = pwm_compute_duty(cnr, cmr, false);
621
622 pwm_write_pcr(qts, td, pcr);
623 g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr);
624 g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr);
625 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr);
626 g_assert_cmpuint(pwm_read_cnr(qts, td), ==, cnr);
627 g_assert_cmpuint(pwm_read_cmr(qts, td), ==, cmr);
628 g_assert_cmpuint(pwm_get_duty(qts, module, pwm),
629 ==, expected_duty);
630 if (expected_duty != 0 && expected_duty != 100) {
631
632 g_assert_cmpuint(pwm_get_freq(qts, module, pwm),
633 ==, expected_freq);
634 }
635
636
637 mft_verify_rpm(qts, td, expected_duty);
638
639
640 expected_duty = pwm_compute_duty(cnr, cmr, true);
641 pwm_write_pcr(qts, td, pcr | CH_INV);
642 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr | CH_INV);
643 g_assert_cmpuint(pwm_get_duty(qts, module, pwm),
644 ==, expected_duty);
645 if (expected_duty != 0 && expected_duty != 100) {
646
647 g_assert_cmpuint(pwm_get_freq(qts, module, pwm),
648 ==, expected_freq);
649 }
650
651 }
652 }
653 }
654 }
655
656 qtest_quit(qts);
657}
658
659static void pwm_add_test(const char *name, const TestData* td,
660 GTestDataFunc fn)
661{
662 g_autofree char *full_name = g_strdup_printf(
663 "npcm7xx_pwm/module[%d]/pwm[%d]/%s", pwm_module_index(td->module),
664 pwm_index(td->pwm), name);
665 qtest_add_data_func(full_name, td, fn);
666}
667#define add_test(name, td) pwm_add_test(#name, td, test_##name)
668
669int main(int argc, char **argv)
670{
671 TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)];
672
673 g_test_init(&argc, &argv, NULL);
674
675 for (int i = 0; i < ARRAY_SIZE(pwm_module_list); ++i) {
676 for (int j = 0; j < ARRAY_SIZE(pwm_list); ++j) {
677 TestData *td = &test_data_list[i * ARRAY_SIZE(pwm_list) + j];
678
679 td->module = &pwm_module_list[i];
680 td->pwm = &pwm_list[j];
681
682 add_test(init, td);
683 add_test(oneshot, td);
684 add_test(toggle, td);
685 }
686 }
687
688 return g_test_run();
689}
690