xref: /qemu/tests/qtest/npcm7xx_pwm-test.c (revision 302585450c667cac06371a80446eedf670d2d510)
173314f13SHao Wu /*
273314f13SHao Wu  * QTests for Nuvoton NPCM7xx PWM Modules.
373314f13SHao Wu  *
473314f13SHao Wu  * Copyright 2020 Google LLC
573314f13SHao Wu  *
673314f13SHao Wu  * This program is free software; you can redistribute it and/or modify it
773314f13SHao Wu  * under the terms of the GNU General Public License as published by the
873314f13SHao Wu  * Free Software Foundation; either version 2 of the License, or
973314f13SHao Wu  * (at your option) any later version.
1073314f13SHao Wu  *
1173314f13SHao Wu  * This program is distributed in the hope that it will be useful, but WITHOUT
1273314f13SHao Wu  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1373314f13SHao Wu  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1473314f13SHao Wu  * for more details.
1573314f13SHao Wu  */
1673314f13SHao Wu 
1773314f13SHao Wu #include "qemu/osdep.h"
1873314f13SHao Wu #include "qemu/bitops.h"
1973314f13SHao Wu #include "libqos/libqtest.h"
2073314f13SHao Wu #include "qapi/qmp/qdict.h"
2173314f13SHao Wu #include "qapi/qmp/qnum.h"
2273314f13SHao Wu 
2373314f13SHao Wu #define REF_HZ          25000000
2473314f13SHao Wu 
2573314f13SHao Wu /* Register field definitions. */
2673314f13SHao Wu #define CH_EN           BIT(0)
2773314f13SHao Wu #define CH_INV          BIT(2)
2873314f13SHao Wu #define CH_MOD          BIT(3)
2973314f13SHao Wu 
3073314f13SHao Wu /* Registers shared between all PWMs in a module */
3173314f13SHao Wu #define PPR             0x00
3273314f13SHao Wu #define CSR             0x04
3373314f13SHao Wu #define PCR             0x08
3473314f13SHao Wu #define PIER            0x3c
3573314f13SHao Wu #define PIIR            0x40
3673314f13SHao Wu 
3773314f13SHao Wu /* CLK module related */
3873314f13SHao Wu #define CLK_BA          0xf0801000
3973314f13SHao Wu #define CLKSEL          0x04
4073314f13SHao Wu #define CLKDIV1         0x08
4173314f13SHao Wu #define CLKDIV2         0x2c
4273314f13SHao Wu #define PLLCON0         0x0c
4373314f13SHao Wu #define PLLCON1         0x10
4473314f13SHao Wu #define PLL_INDV(rv)    extract32((rv), 0, 6)
4573314f13SHao Wu #define PLL_FBDV(rv)    extract32((rv), 16, 12)
4673314f13SHao Wu #define PLL_OTDV1(rv)   extract32((rv), 8, 3)
4773314f13SHao Wu #define PLL_OTDV2(rv)   extract32((rv), 13, 3)
48999be4a2SHao Wu #define APB4CKDIV(rv)   extract32((rv), 30, 2)
4973314f13SHao Wu #define APB3CKDIV(rv)   extract32((rv), 28, 2)
5073314f13SHao Wu #define CLK2CKDIV(rv)   extract32((rv), 0, 1)
5173314f13SHao Wu #define CLK4CKDIV(rv)   extract32((rv), 26, 2)
5273314f13SHao Wu #define CPUCKSEL(rv)    extract32((rv), 0, 2)
5373314f13SHao Wu 
5473314f13SHao Wu #define MAX_DUTY        1000000
5573314f13SHao Wu 
56999be4a2SHao Wu /* MFT (PWM fan) related */
57999be4a2SHao Wu #define MFT_BA(n)       (0xf0180000 + ((n) * 0x1000))
58999be4a2SHao Wu #define MFT_IRQ(n)      (96 + (n))
59999be4a2SHao Wu #define MFT_CNT1        0x00
60999be4a2SHao Wu #define MFT_CRA         0x02
61999be4a2SHao Wu #define MFT_CRB         0x04
62999be4a2SHao Wu #define MFT_CNT2        0x06
63999be4a2SHao Wu #define MFT_PRSC        0x08
64999be4a2SHao Wu #define MFT_CKC         0x0a
65999be4a2SHao Wu #define MFT_MCTRL       0x0c
66999be4a2SHao Wu #define MFT_ICTRL       0x0e
67999be4a2SHao Wu #define MFT_ICLR        0x10
68999be4a2SHao Wu #define MFT_IEN         0x12
69999be4a2SHao Wu #define MFT_CPA         0x14
70999be4a2SHao Wu #define MFT_CPB         0x16
71999be4a2SHao Wu #define MFT_CPCFG       0x18
72999be4a2SHao Wu #define MFT_INASEL      0x1a
73999be4a2SHao Wu #define MFT_INBSEL      0x1c
74999be4a2SHao Wu 
75999be4a2SHao Wu #define MFT_MCTRL_ALL   0x64
76999be4a2SHao Wu #define MFT_ICLR_ALL    0x3f
77999be4a2SHao Wu #define MFT_IEN_ALL     0x3f
78999be4a2SHao Wu #define MFT_CPCFG_EQ_MODE 0x44
79999be4a2SHao Wu 
80999be4a2SHao Wu #define MFT_CKC_C2CSEL  BIT(3)
81999be4a2SHao Wu #define MFT_CKC_C1CSEL  BIT(0)
82999be4a2SHao Wu 
83999be4a2SHao Wu #define MFT_ICTRL_TFPND BIT(5)
84999be4a2SHao Wu #define MFT_ICTRL_TEPND BIT(4)
85999be4a2SHao Wu #define MFT_ICTRL_TDPND BIT(3)
86999be4a2SHao Wu #define MFT_ICTRL_TCPND BIT(2)
87999be4a2SHao Wu #define MFT_ICTRL_TBPND BIT(1)
88999be4a2SHao Wu #define MFT_ICTRL_TAPND BIT(0)
89999be4a2SHao Wu 
90999be4a2SHao Wu #define MFT_MAX_CNT     0xffff
91999be4a2SHao Wu #define MFT_TIMEOUT     0x5000
92999be4a2SHao Wu 
93999be4a2SHao Wu #define DEFAULT_RPM     19800
94999be4a2SHao Wu #define DEFAULT_PRSC    255
95999be4a2SHao Wu #define MFT_PULSE_PER_REVOLUTION 2
96999be4a2SHao Wu 
97999be4a2SHao Wu #define MAX_ERROR       1
98999be4a2SHao Wu 
9973314f13SHao Wu typedef struct PWMModule {
10073314f13SHao Wu     int irq;
10173314f13SHao Wu     uint64_t base_addr;
10273314f13SHao Wu } PWMModule;
10373314f13SHao Wu 
10473314f13SHao Wu typedef struct PWM {
10573314f13SHao Wu     uint32_t cnr_offset;
10673314f13SHao Wu     uint32_t cmr_offset;
10773314f13SHao Wu     uint32_t pdr_offset;
10873314f13SHao Wu     uint32_t pwdr_offset;
10973314f13SHao Wu } PWM;
11073314f13SHao Wu 
11173314f13SHao Wu typedef struct TestData {
11273314f13SHao Wu     const PWMModule *module;
11373314f13SHao Wu     const PWM *pwm;
11473314f13SHao Wu } TestData;
11573314f13SHao Wu 
11673314f13SHao Wu static const PWMModule pwm_module_list[] = {
11773314f13SHao Wu     {
11873314f13SHao Wu         .irq        = 93,
11973314f13SHao Wu         .base_addr  = 0xf0103000
12073314f13SHao Wu     },
12173314f13SHao Wu     {
12273314f13SHao Wu         .irq        = 94,
12373314f13SHao Wu         .base_addr  = 0xf0104000
12473314f13SHao Wu     }
12573314f13SHao Wu };
12673314f13SHao Wu 
12773314f13SHao Wu static const PWM pwm_list[] = {
12873314f13SHao Wu     {
12973314f13SHao Wu         .cnr_offset     = 0x0c,
13073314f13SHao Wu         .cmr_offset     = 0x10,
13173314f13SHao Wu         .pdr_offset     = 0x14,
13273314f13SHao Wu         .pwdr_offset    = 0x44,
13373314f13SHao Wu     },
13473314f13SHao Wu     {
13573314f13SHao Wu         .cnr_offset     = 0x18,
13673314f13SHao Wu         .cmr_offset     = 0x1c,
13773314f13SHao Wu         .pdr_offset     = 0x20,
13873314f13SHao Wu         .pwdr_offset    = 0x48,
13973314f13SHao Wu     },
14073314f13SHao Wu     {
14173314f13SHao Wu         .cnr_offset     = 0x24,
14273314f13SHao Wu         .cmr_offset     = 0x28,
14373314f13SHao Wu         .pdr_offset     = 0x2c,
14473314f13SHao Wu         .pwdr_offset    = 0x4c,
14573314f13SHao Wu     },
14673314f13SHao Wu     {
14773314f13SHao Wu         .cnr_offset     = 0x30,
14873314f13SHao Wu         .cmr_offset     = 0x34,
14973314f13SHao Wu         .pdr_offset     = 0x38,
15073314f13SHao Wu         .pwdr_offset    = 0x50,
15173314f13SHao Wu     },
15273314f13SHao Wu };
15373314f13SHao Wu 
15473314f13SHao Wu static const int ppr_base[] = { 0, 0, 8, 8 };
15573314f13SHao Wu static const int csr_base[] = { 0, 4, 8, 12 };
15673314f13SHao Wu static const int pcr_base[] = { 0, 8, 12, 16 };
15773314f13SHao Wu 
15873314f13SHao Wu static const uint32_t ppr_list[] = {
15973314f13SHao Wu     0,
16073314f13SHao Wu     1,
16173314f13SHao Wu     10,
16273314f13SHao Wu     100,
16373314f13SHao Wu     255, /* Max possible value. */
16473314f13SHao Wu };
16573314f13SHao Wu 
16673314f13SHao Wu static const uint32_t csr_list[] = {
16773314f13SHao Wu     0,
16873314f13SHao Wu     1,
16973314f13SHao Wu     2,
17073314f13SHao Wu     3,
17173314f13SHao Wu     4, /* Max possible value. */
17273314f13SHao Wu };
17373314f13SHao Wu 
17473314f13SHao Wu static const uint32_t cnr_list[] = {
17573314f13SHao Wu     0,
17673314f13SHao Wu     1,
17773314f13SHao Wu     50,
17873314f13SHao Wu     100,
17973314f13SHao Wu     150,
18073314f13SHao Wu     200,
18173314f13SHao Wu     1000,
18273314f13SHao Wu     10000,
18373314f13SHao Wu     65535, /* Max possible value. */
18473314f13SHao Wu };
18573314f13SHao Wu 
18673314f13SHao Wu static const uint32_t cmr_list[] = {
18773314f13SHao Wu     0,
18873314f13SHao Wu     1,
18973314f13SHao Wu     10,
19073314f13SHao Wu     50,
19173314f13SHao Wu     100,
19273314f13SHao Wu     150,
19373314f13SHao Wu     200,
19473314f13SHao Wu     1000,
19573314f13SHao Wu     10000,
19673314f13SHao Wu     65535, /* Max possible value. */
19773314f13SHao Wu };
19873314f13SHao Wu 
19973314f13SHao Wu /* Returns the index of the PWM module. */
20073314f13SHao Wu static int pwm_module_index(const PWMModule *module)
20173314f13SHao Wu {
20273314f13SHao Wu     ptrdiff_t diff = module - pwm_module_list;
20373314f13SHao Wu 
204*30258545SPeter Maydell     g_assert(diff >= 0 && diff < ARRAY_SIZE(pwm_module_list));
20573314f13SHao Wu 
20673314f13SHao Wu     return diff;
20773314f13SHao Wu }
20873314f13SHao Wu 
20973314f13SHao Wu /* Returns the index of the PWM entry. */
21073314f13SHao Wu static int pwm_index(const PWM *pwm)
21173314f13SHao Wu {
21273314f13SHao Wu     ptrdiff_t diff = pwm - pwm_list;
21373314f13SHao Wu 
214*30258545SPeter Maydell     g_assert(diff >= 0 && diff < ARRAY_SIZE(pwm_list));
21573314f13SHao Wu 
21673314f13SHao Wu     return diff;
21773314f13SHao Wu }
21873314f13SHao Wu 
21973314f13SHao Wu static uint64_t pwm_qom_get(QTestState *qts, const char *path, const char *name)
22073314f13SHao Wu {
22173314f13SHao Wu     QDict *response;
2223e829c04SGan Qixin     uint64_t val;
22373314f13SHao Wu 
22473314f13SHao Wu     g_test_message("Getting properties %s from %s", name, path);
22573314f13SHao Wu     response = qtest_qmp(qts, "{ 'execute': 'qom-get',"
22673314f13SHao Wu             " 'arguments': { 'path': %s, 'property': %s}}",
22773314f13SHao Wu             path, name);
22873314f13SHao Wu     /* The qom set message returns successfully. */
22973314f13SHao Wu     g_assert_true(qdict_haskey(response, "return"));
2303e829c04SGan Qixin     val = qnum_get_uint(qobject_to(QNum, qdict_get(response, "return")));
2313e829c04SGan Qixin     qobject_unref(response);
2323e829c04SGan Qixin     return val;
23373314f13SHao Wu }
23473314f13SHao Wu 
23573314f13SHao Wu static uint64_t pwm_get_freq(QTestState *qts, int module_index, int pwm_index)
23673314f13SHao Wu {
23773314f13SHao Wu     char path[100];
23873314f13SHao Wu     char name[100];
23973314f13SHao Wu 
24073314f13SHao Wu     sprintf(path, "/machine/soc/pwm[%d]", module_index);
24173314f13SHao Wu     sprintf(name, "freq[%d]", pwm_index);
24273314f13SHao Wu 
24373314f13SHao Wu     return pwm_qom_get(qts, path, name);
24473314f13SHao Wu }
24573314f13SHao Wu 
24673314f13SHao Wu static uint64_t pwm_get_duty(QTestState *qts, int module_index, int pwm_index)
24773314f13SHao Wu {
24873314f13SHao Wu     char path[100];
24973314f13SHao Wu     char name[100];
25073314f13SHao Wu 
25173314f13SHao Wu     sprintf(path, "/machine/soc/pwm[%d]", module_index);
25273314f13SHao Wu     sprintf(name, "duty[%d]", pwm_index);
25373314f13SHao Wu 
25473314f13SHao Wu     return pwm_qom_get(qts, path, name);
25573314f13SHao Wu }
25673314f13SHao Wu 
257999be4a2SHao Wu static void mft_qom_set(QTestState *qts, int index, const char *name,
258999be4a2SHao Wu                         uint32_t value)
259999be4a2SHao Wu {
260999be4a2SHao Wu     QDict *response;
261999be4a2SHao Wu     char *path = g_strdup_printf("/machine/soc/mft[%d]", index);
262999be4a2SHao Wu 
263999be4a2SHao Wu     g_test_message("Setting properties %s of mft[%d] with value %u",
264999be4a2SHao Wu                    name, index, value);
265999be4a2SHao Wu     response = qtest_qmp(qts, "{ 'execute': 'qom-set',"
266999be4a2SHao Wu             " 'arguments': { 'path': %s, "
267999be4a2SHao Wu             " 'property': %s, 'value': %u}}",
268999be4a2SHao Wu             path, name, value);
269999be4a2SHao Wu     /* The qom set message returns successfully. */
270999be4a2SHao Wu     g_assert_true(qdict_haskey(response, "return"));
271999be4a2SHao Wu }
272999be4a2SHao Wu 
27373314f13SHao Wu static uint32_t get_pll(uint32_t con)
27473314f13SHao Wu {
27573314f13SHao Wu     return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con)
27673314f13SHao Wu             * PLL_OTDV2(con));
27773314f13SHao Wu }
27873314f13SHao Wu 
279999be4a2SHao Wu static uint64_t read_pclk(QTestState *qts, bool mft)
28073314f13SHao Wu {
28173314f13SHao Wu     uint64_t freq = REF_HZ;
28273314f13SHao Wu     uint32_t clksel = qtest_readl(qts, CLK_BA + CLKSEL);
28373314f13SHao Wu     uint32_t pllcon;
28473314f13SHao Wu     uint32_t clkdiv1 = qtest_readl(qts, CLK_BA + CLKDIV1);
28573314f13SHao Wu     uint32_t clkdiv2 = qtest_readl(qts, CLK_BA + CLKDIV2);
286999be4a2SHao Wu     uint32_t apbdiv = mft ? APB4CKDIV(clkdiv2) : APB3CKDIV(clkdiv2);
28773314f13SHao Wu 
28873314f13SHao Wu     switch (CPUCKSEL(clksel)) {
28973314f13SHao Wu     case 0:
29073314f13SHao Wu         pllcon = qtest_readl(qts, CLK_BA + PLLCON0);
29173314f13SHao Wu         freq = get_pll(pllcon);
29273314f13SHao Wu         break;
29373314f13SHao Wu     case 1:
29473314f13SHao Wu         pllcon = qtest_readl(qts, CLK_BA + PLLCON1);
29573314f13SHao Wu         freq = get_pll(pllcon);
29673314f13SHao Wu         break;
29773314f13SHao Wu     case 2:
29873314f13SHao Wu         break;
29973314f13SHao Wu     case 3:
30073314f13SHao Wu         break;
30173314f13SHao Wu     default:
30273314f13SHao Wu         g_assert_not_reached();
30373314f13SHao Wu     }
30473314f13SHao Wu 
305999be4a2SHao Wu     freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + apbdiv);
30673314f13SHao Wu 
30773314f13SHao Wu     return freq;
30873314f13SHao Wu }
30973314f13SHao Wu 
31073314f13SHao Wu static uint32_t pwm_selector(uint32_t csr)
31173314f13SHao Wu {
31273314f13SHao Wu     switch (csr) {
31373314f13SHao Wu     case 0:
31473314f13SHao Wu         return 2;
31573314f13SHao Wu     case 1:
31673314f13SHao Wu         return 4;
31773314f13SHao Wu     case 2:
31873314f13SHao Wu         return 8;
31973314f13SHao Wu     case 3:
32073314f13SHao Wu         return 16;
32173314f13SHao Wu     case 4:
32273314f13SHao Wu         return 1;
32373314f13SHao Wu     default:
32473314f13SHao Wu         g_assert_not_reached();
32573314f13SHao Wu     }
32673314f13SHao Wu }
32773314f13SHao Wu 
32873314f13SHao Wu static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr,
32973314f13SHao Wu         uint32_t cnr)
33073314f13SHao Wu {
331999be4a2SHao Wu     return read_pclk(qts, false) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1));
33273314f13SHao Wu }
33373314f13SHao Wu 
33473314f13SHao Wu static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted)
33573314f13SHao Wu {
3361e5ce6e1SHao Wu     uint32_t duty;
33773314f13SHao Wu 
33873314f13SHao Wu     if (cnr == 0) {
33973314f13SHao Wu         /* PWM is stopped. */
34073314f13SHao Wu         duty = 0;
34173314f13SHao Wu     } else if (cmr >= cnr) {
34273314f13SHao Wu         duty = MAX_DUTY;
34373314f13SHao Wu     } else {
3441e5ce6e1SHao Wu         duty = (uint64_t)MAX_DUTY * (cmr + 1) / (cnr + 1);
34573314f13SHao Wu     }
34673314f13SHao Wu 
34773314f13SHao Wu     if (inverted) {
34873314f13SHao Wu         duty = MAX_DUTY - duty;
34973314f13SHao Wu     }
35073314f13SHao Wu 
35173314f13SHao Wu     return duty;
35273314f13SHao Wu }
35373314f13SHao Wu 
35473314f13SHao Wu static uint32_t pwm_read(QTestState *qts, const TestData *td, unsigned offset)
35573314f13SHao Wu {
35673314f13SHao Wu     return qtest_readl(qts, td->module->base_addr + offset);
35773314f13SHao Wu }
35873314f13SHao Wu 
35973314f13SHao Wu static void pwm_write(QTestState *qts, const TestData *td, unsigned offset,
36073314f13SHao Wu         uint32_t value)
36173314f13SHao Wu {
36273314f13SHao Wu     qtest_writel(qts, td->module->base_addr + offset, value);
36373314f13SHao Wu }
36473314f13SHao Wu 
365999be4a2SHao Wu static uint8_t mft_readb(QTestState *qts, int index, unsigned offset)
366999be4a2SHao Wu {
367999be4a2SHao Wu     return qtest_readb(qts, MFT_BA(index) + offset);
368999be4a2SHao Wu }
369999be4a2SHao Wu 
370999be4a2SHao Wu static uint16_t mft_readw(QTestState *qts, int index, unsigned offset)
371999be4a2SHao Wu {
372999be4a2SHao Wu     return qtest_readw(qts, MFT_BA(index) + offset);
373999be4a2SHao Wu }
374999be4a2SHao Wu 
375999be4a2SHao Wu static void mft_writeb(QTestState *qts, int index, unsigned offset,
376999be4a2SHao Wu                         uint8_t value)
377999be4a2SHao Wu {
378999be4a2SHao Wu     qtest_writeb(qts, MFT_BA(index) + offset, value);
379999be4a2SHao Wu }
380999be4a2SHao Wu 
381999be4a2SHao Wu static void mft_writew(QTestState *qts, int index, unsigned offset,
382999be4a2SHao Wu                         uint16_t value)
383999be4a2SHao Wu {
384999be4a2SHao Wu     return qtest_writew(qts, MFT_BA(index) + offset, value);
385999be4a2SHao Wu }
386999be4a2SHao Wu 
38773314f13SHao Wu static uint32_t pwm_read_ppr(QTestState *qts, const TestData *td)
38873314f13SHao Wu {
38973314f13SHao Wu     return extract32(pwm_read(qts, td, PPR), ppr_base[pwm_index(td->pwm)], 8);
39073314f13SHao Wu }
39173314f13SHao Wu 
39273314f13SHao Wu static void pwm_write_ppr(QTestState *qts, const TestData *td, uint32_t value)
39373314f13SHao Wu {
39473314f13SHao Wu     pwm_write(qts, td, PPR, value << ppr_base[pwm_index(td->pwm)]);
39573314f13SHao Wu }
39673314f13SHao Wu 
39773314f13SHao Wu static uint32_t pwm_read_csr(QTestState *qts, const TestData *td)
39873314f13SHao Wu {
39973314f13SHao Wu     return extract32(pwm_read(qts, td, CSR), csr_base[pwm_index(td->pwm)], 3);
40073314f13SHao Wu }
40173314f13SHao Wu 
40273314f13SHao Wu static void pwm_write_csr(QTestState *qts, const TestData *td, uint32_t value)
40373314f13SHao Wu {
40473314f13SHao Wu     pwm_write(qts, td, CSR, value << csr_base[pwm_index(td->pwm)]);
40573314f13SHao Wu }
40673314f13SHao Wu 
40773314f13SHao Wu static uint32_t pwm_read_pcr(QTestState *qts, const TestData *td)
40873314f13SHao Wu {
40973314f13SHao Wu     return extract32(pwm_read(qts, td, PCR), pcr_base[pwm_index(td->pwm)], 4);
41073314f13SHao Wu }
41173314f13SHao Wu 
41273314f13SHao Wu static void pwm_write_pcr(QTestState *qts, const TestData *td, uint32_t value)
41373314f13SHao Wu {
41473314f13SHao Wu     pwm_write(qts, td, PCR, value << pcr_base[pwm_index(td->pwm)]);
41573314f13SHao Wu }
41673314f13SHao Wu 
41773314f13SHao Wu static uint32_t pwm_read_cnr(QTestState *qts, const TestData *td)
41873314f13SHao Wu {
41973314f13SHao Wu     return pwm_read(qts, td, td->pwm->cnr_offset);
42073314f13SHao Wu }
42173314f13SHao Wu 
42273314f13SHao Wu static void pwm_write_cnr(QTestState *qts, const TestData *td, uint32_t value)
42373314f13SHao Wu {
42473314f13SHao Wu     pwm_write(qts, td, td->pwm->cnr_offset, value);
42573314f13SHao Wu }
42673314f13SHao Wu 
42773314f13SHao Wu static uint32_t pwm_read_cmr(QTestState *qts, const TestData *td)
42873314f13SHao Wu {
42973314f13SHao Wu     return pwm_read(qts, td, td->pwm->cmr_offset);
43073314f13SHao Wu }
43173314f13SHao Wu 
43273314f13SHao Wu static void pwm_write_cmr(QTestState *qts, const TestData *td, uint32_t value)
43373314f13SHao Wu {
43473314f13SHao Wu     pwm_write(qts, td, td->pwm->cmr_offset, value);
43573314f13SHao Wu }
43673314f13SHao Wu 
437999be4a2SHao Wu static int mft_compute_index(const TestData *td)
438999be4a2SHao Wu {
439999be4a2SHao Wu     int index = pwm_module_index(td->module) * ARRAY_SIZE(pwm_list) +
440999be4a2SHao Wu                 pwm_index(td->pwm);
441999be4a2SHao Wu 
442999be4a2SHao Wu     g_assert_cmpint(index, <,
443999be4a2SHao Wu                     ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list));
444999be4a2SHao Wu 
445999be4a2SHao Wu     return index;
446999be4a2SHao Wu }
447999be4a2SHao Wu 
448999be4a2SHao Wu static void mft_reset_counters(QTestState *qts, int index)
449999be4a2SHao Wu {
450999be4a2SHao Wu     mft_writew(qts, index, MFT_CNT1, MFT_MAX_CNT);
451999be4a2SHao Wu     mft_writew(qts, index, MFT_CNT2, MFT_MAX_CNT);
452999be4a2SHao Wu     mft_writew(qts, index, MFT_CRA, MFT_MAX_CNT);
453999be4a2SHao Wu     mft_writew(qts, index, MFT_CRB, MFT_MAX_CNT);
454999be4a2SHao Wu     mft_writew(qts, index, MFT_CPA, MFT_MAX_CNT - MFT_TIMEOUT);
455999be4a2SHao Wu     mft_writew(qts, index, MFT_CPB, MFT_MAX_CNT - MFT_TIMEOUT);
456999be4a2SHao Wu }
457999be4a2SHao Wu 
458999be4a2SHao Wu static void mft_init(QTestState *qts, const TestData *td)
459999be4a2SHao Wu {
460999be4a2SHao Wu     int index = mft_compute_index(td);
461999be4a2SHao Wu 
462999be4a2SHao Wu     /* Enable everything */
463999be4a2SHao Wu     mft_writeb(qts, index, MFT_CKC, 0);
464999be4a2SHao Wu     mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL);
465999be4a2SHao Wu     mft_writeb(qts, index, MFT_MCTRL, MFT_MCTRL_ALL);
466999be4a2SHao Wu     mft_writeb(qts, index, MFT_IEN, MFT_IEN_ALL);
467999be4a2SHao Wu     mft_writeb(qts, index, MFT_INASEL, 0);
468999be4a2SHao Wu     mft_writeb(qts, index, MFT_INBSEL, 0);
469999be4a2SHao Wu 
470999be4a2SHao Wu     /* Set cpcfg to use EQ mode, same as kernel driver */
471999be4a2SHao Wu     mft_writeb(qts, index, MFT_CPCFG, MFT_CPCFG_EQ_MODE);
472999be4a2SHao Wu 
473999be4a2SHao Wu     /* Write default counters, timeout and prescaler */
474999be4a2SHao Wu     mft_reset_counters(qts, index);
475999be4a2SHao Wu     mft_writeb(qts, index, MFT_PRSC, DEFAULT_PRSC);
476999be4a2SHao Wu 
477999be4a2SHao Wu     /* Write default max rpm via QMP */
478999be4a2SHao Wu     mft_qom_set(qts, index, "max_rpm[0]", DEFAULT_RPM);
479999be4a2SHao Wu     mft_qom_set(qts, index, "max_rpm[1]", DEFAULT_RPM);
480999be4a2SHao Wu }
481999be4a2SHao Wu 
482999be4a2SHao Wu static int32_t mft_compute_cnt(uint32_t rpm, uint64_t clk)
483999be4a2SHao Wu {
484999be4a2SHao Wu     uint64_t cnt;
485999be4a2SHao Wu 
486999be4a2SHao Wu     if (rpm == 0) {
487999be4a2SHao Wu         return -1;
488999be4a2SHao Wu     }
489999be4a2SHao Wu 
490999be4a2SHao Wu     cnt = clk * 60 / ((DEFAULT_PRSC + 1) * rpm * MFT_PULSE_PER_REVOLUTION);
491999be4a2SHao Wu     if (cnt >= MFT_TIMEOUT) {
492999be4a2SHao Wu         return -1;
493999be4a2SHao Wu     }
494999be4a2SHao Wu     return MFT_MAX_CNT - cnt;
495999be4a2SHao Wu }
496999be4a2SHao Wu 
497999be4a2SHao Wu static void mft_verify_rpm(QTestState *qts, const TestData *td, uint64_t duty)
498999be4a2SHao Wu {
499999be4a2SHao Wu     int index = mft_compute_index(td);
500999be4a2SHao Wu     uint16_t cnt, cr;
501999be4a2SHao Wu     uint32_t rpm = DEFAULT_RPM * duty / MAX_DUTY;
502999be4a2SHao Wu     uint64_t clk = read_pclk(qts, true);
503999be4a2SHao Wu     int32_t expected_cnt = mft_compute_cnt(rpm, clk);
504999be4a2SHao Wu 
505999be4a2SHao Wu     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
506999be4a2SHao Wu     g_test_message(
507999be4a2SHao Wu         "verifying rpm for mft[%d]: clk: %" PRIu64 ", duty: %" PRIu64 ", rpm: %u, cnt: %d",
508999be4a2SHao Wu         index, clk, duty, rpm, expected_cnt);
509999be4a2SHao Wu 
510999be4a2SHao Wu     /* Verify rpm for fan A */
511999be4a2SHao Wu     /* Stop capture */
512999be4a2SHao Wu     mft_writeb(qts, index, MFT_CKC, 0);
513999be4a2SHao Wu     mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL);
514999be4a2SHao Wu     mft_reset_counters(qts, index);
515999be4a2SHao Wu     g_assert_cmphex(mft_readw(qts, index, MFT_CNT1), ==, MFT_MAX_CNT);
516999be4a2SHao Wu     g_assert_cmphex(mft_readw(qts, index, MFT_CRA), ==, MFT_MAX_CNT);
517999be4a2SHao Wu     g_assert_cmphex(mft_readw(qts, index, MFT_CPA), ==,
518999be4a2SHao Wu                     MFT_MAX_CNT - MFT_TIMEOUT);
519999be4a2SHao Wu     /* Start capture */
520999be4a2SHao Wu     mft_writeb(qts, index, MFT_CKC, MFT_CKC_C1CSEL);
521999be4a2SHao Wu     g_assert_true(qtest_get_irq(qts, MFT_IRQ(index)));
522999be4a2SHao Wu     if (expected_cnt == -1) {
523999be4a2SHao Wu         g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TEPND);
524999be4a2SHao Wu     } else {
525999be4a2SHao Wu         g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TAPND);
526999be4a2SHao Wu         cnt = mft_readw(qts, index, MFT_CNT1);
527999be4a2SHao Wu         /*
528999be4a2SHao Wu          * Due to error in clock measurement and rounding, we might have a small
529999be4a2SHao Wu          * error in measuring RPM.
530999be4a2SHao Wu          */
531999be4a2SHao Wu         g_assert_cmphex(cnt + MAX_ERROR, >=, expected_cnt);
532999be4a2SHao Wu         g_assert_cmphex(cnt, <=, expected_cnt + MAX_ERROR);
533999be4a2SHao Wu         cr = mft_readw(qts, index, MFT_CRA);
534999be4a2SHao Wu         g_assert_cmphex(cnt, ==, cr);
535999be4a2SHao Wu     }
536999be4a2SHao Wu 
537999be4a2SHao Wu     /* Verify rpm for fan B */
538999be4a2SHao Wu 
539999be4a2SHao Wu     qtest_irq_intercept_out(qts, "/machine/soc/a9mpcore/gic");
540999be4a2SHao Wu }
541999be4a2SHao Wu 
54273314f13SHao Wu /* Check pwm registers can be reset to default value */
54373314f13SHao Wu static void test_init(gconstpointer test_data)
54473314f13SHao Wu {
54573314f13SHao Wu     const TestData *td = test_data;
546999be4a2SHao Wu     QTestState *qts = qtest_init("-machine npcm750-evb");
54773314f13SHao Wu     int module = pwm_module_index(td->module);
54873314f13SHao Wu     int pwm = pwm_index(td->pwm);
54973314f13SHao Wu 
55073314f13SHao Wu     g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0);
55173314f13SHao Wu     g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0);
55273314f13SHao Wu 
55373314f13SHao Wu     qtest_quit(qts);
55473314f13SHao Wu }
55573314f13SHao Wu 
55673314f13SHao Wu /* One-shot mode should not change frequency and duty cycle. */
55773314f13SHao Wu static void test_oneshot(gconstpointer test_data)
55873314f13SHao Wu {
55973314f13SHao Wu     const TestData *td = test_data;
560999be4a2SHao Wu     QTestState *qts = qtest_init("-machine npcm750-evb");
56173314f13SHao Wu     int module = pwm_module_index(td->module);
56273314f13SHao Wu     int pwm = pwm_index(td->pwm);
56373314f13SHao Wu     uint32_t ppr, csr, pcr;
56473314f13SHao Wu     int i, j;
56573314f13SHao Wu 
56673314f13SHao Wu     pcr = CH_EN;
56773314f13SHao Wu     for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) {
56873314f13SHao Wu         ppr = ppr_list[i];
56973314f13SHao Wu         pwm_write_ppr(qts, td, ppr);
57073314f13SHao Wu 
57173314f13SHao Wu         for (j = 0; j < ARRAY_SIZE(csr_list); ++j) {
57273314f13SHao Wu             csr = csr_list[j];
57373314f13SHao Wu             pwm_write_csr(qts, td, csr);
57473314f13SHao Wu             pwm_write_pcr(qts, td, pcr);
57573314f13SHao Wu 
57673314f13SHao Wu             g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr);
57773314f13SHao Wu             g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr);
57873314f13SHao Wu             g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr);
57973314f13SHao Wu             g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0);
58073314f13SHao Wu             g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0);
58173314f13SHao Wu         }
58273314f13SHao Wu     }
58373314f13SHao Wu 
58473314f13SHao Wu     qtest_quit(qts);
58573314f13SHao Wu }
58673314f13SHao Wu 
58773314f13SHao Wu /* In toggle mode, the PWM generates correct outputs. */
58873314f13SHao Wu static void test_toggle(gconstpointer test_data)
58973314f13SHao Wu {
59073314f13SHao Wu     const TestData *td = test_data;
591999be4a2SHao Wu     QTestState *qts = qtest_init("-machine npcm750-evb");
59273314f13SHao Wu     int module = pwm_module_index(td->module);
59373314f13SHao Wu     int pwm = pwm_index(td->pwm);
59473314f13SHao Wu     uint32_t ppr, csr, pcr, cnr, cmr;
59573314f13SHao Wu     int i, j, k, l;
59673314f13SHao Wu     uint64_t expected_freq, expected_duty;
59773314f13SHao Wu 
598999be4a2SHao Wu     mft_init(qts, td);
599999be4a2SHao Wu 
60073314f13SHao Wu     pcr = CH_EN | CH_MOD;
60173314f13SHao Wu     for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) {
60273314f13SHao Wu         ppr = ppr_list[i];
60373314f13SHao Wu         pwm_write_ppr(qts, td, ppr);
60473314f13SHao Wu 
60573314f13SHao Wu         for (j = 0; j < ARRAY_SIZE(csr_list); ++j) {
60673314f13SHao Wu             csr = csr_list[j];
60773314f13SHao Wu             pwm_write_csr(qts, td, csr);
60873314f13SHao Wu 
60973314f13SHao Wu             for (k = 0; k < ARRAY_SIZE(cnr_list); ++k) {
61073314f13SHao Wu                 cnr = cnr_list[k];
61173314f13SHao Wu                 pwm_write_cnr(qts, td, cnr);
61273314f13SHao Wu 
61373314f13SHao Wu                 for (l = 0; l < ARRAY_SIZE(cmr_list); ++l) {
61473314f13SHao Wu                     cmr = cmr_list[l];
61573314f13SHao Wu                     pwm_write_cmr(qts, td, cmr);
61673314f13SHao Wu                     expected_freq = pwm_compute_freq(qts, ppr, csr, cnr);
61773314f13SHao Wu                     expected_duty = pwm_compute_duty(cnr, cmr, false);
61873314f13SHao Wu 
61973314f13SHao Wu                     pwm_write_pcr(qts, td, pcr);
62073314f13SHao Wu                     g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr);
62173314f13SHao Wu                     g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr);
62273314f13SHao Wu                     g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr);
62373314f13SHao Wu                     g_assert_cmpuint(pwm_read_cnr(qts, td), ==, cnr);
62473314f13SHao Wu                     g_assert_cmpuint(pwm_read_cmr(qts, td), ==, cmr);
62573314f13SHao Wu                     g_assert_cmpuint(pwm_get_duty(qts, module, pwm),
62673314f13SHao Wu                             ==, expected_duty);
62773314f13SHao Wu                     if (expected_duty != 0 && expected_duty != 100) {
62873314f13SHao Wu                         /* Duty cycle with 0 or 100 doesn't need frequency. */
62973314f13SHao Wu                         g_assert_cmpuint(pwm_get_freq(qts, module, pwm),
63073314f13SHao Wu                                 ==, expected_freq);
63173314f13SHao Wu                     }
63273314f13SHao Wu 
633999be4a2SHao Wu                     /* Test MFT's RPM is correct. */
634999be4a2SHao Wu                     mft_verify_rpm(qts, td, expected_duty);
635999be4a2SHao Wu 
63673314f13SHao Wu                     /* Test inverted mode */
63773314f13SHao Wu                     expected_duty = pwm_compute_duty(cnr, cmr, true);
63873314f13SHao Wu                     pwm_write_pcr(qts, td, pcr | CH_INV);
63973314f13SHao Wu                     g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr | CH_INV);
64073314f13SHao Wu                     g_assert_cmpuint(pwm_get_duty(qts, module, pwm),
64173314f13SHao Wu                             ==, expected_duty);
64273314f13SHao Wu                     if (expected_duty != 0 && expected_duty != 100) {
64373314f13SHao Wu                         /* Duty cycle with 0 or 100 doesn't need frequency. */
64473314f13SHao Wu                         g_assert_cmpuint(pwm_get_freq(qts, module, pwm),
64573314f13SHao Wu                                 ==, expected_freq);
64673314f13SHao Wu                     }
64773314f13SHao Wu 
64873314f13SHao Wu                 }
64973314f13SHao Wu             }
65073314f13SHao Wu         }
65173314f13SHao Wu     }
65273314f13SHao Wu 
65373314f13SHao Wu     qtest_quit(qts);
65473314f13SHao Wu }
65573314f13SHao Wu 
65673314f13SHao Wu static void pwm_add_test(const char *name, const TestData* td,
65773314f13SHao Wu         GTestDataFunc fn)
65873314f13SHao Wu {
65973314f13SHao Wu     g_autofree char *full_name = g_strdup_printf(
66073314f13SHao Wu             "npcm7xx_pwm/module[%d]/pwm[%d]/%s", pwm_module_index(td->module),
66173314f13SHao Wu             pwm_index(td->pwm), name);
66273314f13SHao Wu     qtest_add_data_func(full_name, td, fn);
66373314f13SHao Wu }
66473314f13SHao Wu #define add_test(name, td) pwm_add_test(#name, td, test_##name)
66573314f13SHao Wu 
66673314f13SHao Wu int main(int argc, char **argv)
66773314f13SHao Wu {
66873314f13SHao Wu     TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)];
66973314f13SHao Wu 
67073314f13SHao Wu     g_test_init(&argc, &argv, NULL);
67173314f13SHao Wu 
67273314f13SHao Wu     for (int i = 0; i < ARRAY_SIZE(pwm_module_list); ++i) {
67373314f13SHao Wu         for (int j = 0; j < ARRAY_SIZE(pwm_list); ++j) {
67473314f13SHao Wu             TestData *td = &test_data_list[i * ARRAY_SIZE(pwm_list) + j];
67573314f13SHao Wu 
67673314f13SHao Wu             td->module = &pwm_module_list[i];
67773314f13SHao Wu             td->pwm = &pwm_list[j];
67873314f13SHao Wu 
67973314f13SHao Wu             add_test(init, td);
68073314f13SHao Wu             add_test(oneshot, td);
68173314f13SHao Wu             add_test(toggle, td);
68273314f13SHao Wu         }
68373314f13SHao Wu     }
68473314f13SHao Wu 
68573314f13SHao Wu     return g_test_run();
68673314f13SHao Wu }
687