xref: /qemu/tests/qtest/npcm7xx_pwm-test.c (revision 54e91d1523b412b4cff7cb36c898fa9dc133e886)
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"
19907b5105SMarc-André Lureau #include "libqtest.h"
20*407bc4bfSDaniel P. Berrangé #include "qobject/qdict.h"
21*407bc4bfSDaniel P. Berrangé #include "qobject/qnum.h"
2273314f13SHao Wu 
2377034bbcSPeter Maydell static int verbosity_level;
2477034bbcSPeter Maydell 
2573314f13SHao Wu #define REF_HZ          25000000
2673314f13SHao Wu 
2773314f13SHao Wu /* Register field definitions. */
2873314f13SHao Wu #define CH_EN           BIT(0)
2973314f13SHao Wu #define CH_INV          BIT(2)
3073314f13SHao Wu #define CH_MOD          BIT(3)
3173314f13SHao Wu 
3273314f13SHao Wu /* Registers shared between all PWMs in a module */
3373314f13SHao Wu #define PPR             0x00
3473314f13SHao Wu #define CSR             0x04
3573314f13SHao Wu #define PCR             0x08
3673314f13SHao Wu #define PIER            0x3c
3773314f13SHao Wu #define PIIR            0x40
3873314f13SHao Wu 
3973314f13SHao Wu /* CLK module related */
4073314f13SHao Wu #define CLK_BA          0xf0801000
4173314f13SHao Wu #define CLKSEL          0x04
4273314f13SHao Wu #define CLKDIV1         0x08
4373314f13SHao Wu #define CLKDIV2         0x2c
4473314f13SHao Wu #define PLLCON0         0x0c
4573314f13SHao Wu #define PLLCON1         0x10
4673314f13SHao Wu #define PLL_INDV(rv)    extract32((rv), 0, 6)
4773314f13SHao Wu #define PLL_FBDV(rv)    extract32((rv), 16, 12)
4873314f13SHao Wu #define PLL_OTDV1(rv)   extract32((rv), 8, 3)
4973314f13SHao Wu #define PLL_OTDV2(rv)   extract32((rv), 13, 3)
50999be4a2SHao Wu #define APB4CKDIV(rv)   extract32((rv), 30, 2)
5173314f13SHao Wu #define APB3CKDIV(rv)   extract32((rv), 28, 2)
5273314f13SHao Wu #define CLK2CKDIV(rv)   extract32((rv), 0, 1)
5373314f13SHao Wu #define CLK4CKDIV(rv)   extract32((rv), 26, 2)
5473314f13SHao Wu #define CPUCKSEL(rv)    extract32((rv), 0, 2)
5573314f13SHao Wu 
5673314f13SHao Wu #define MAX_DUTY        1000000
5773314f13SHao Wu 
58999be4a2SHao Wu /* MFT (PWM fan) related */
59999be4a2SHao Wu #define MFT_BA(n)       (0xf0180000 + ((n) * 0x1000))
60999be4a2SHao Wu #define MFT_IRQ(n)      (96 + (n))
61999be4a2SHao Wu #define MFT_CNT1        0x00
62999be4a2SHao Wu #define MFT_CRA         0x02
63999be4a2SHao Wu #define MFT_CRB         0x04
64999be4a2SHao Wu #define MFT_CNT2        0x06
65999be4a2SHao Wu #define MFT_PRSC        0x08
66999be4a2SHao Wu #define MFT_CKC         0x0a
67999be4a2SHao Wu #define MFT_MCTRL       0x0c
68999be4a2SHao Wu #define MFT_ICTRL       0x0e
69999be4a2SHao Wu #define MFT_ICLR        0x10
70999be4a2SHao Wu #define MFT_IEN         0x12
71999be4a2SHao Wu #define MFT_CPA         0x14
72999be4a2SHao Wu #define MFT_CPB         0x16
73999be4a2SHao Wu #define MFT_CPCFG       0x18
74999be4a2SHao Wu #define MFT_INASEL      0x1a
75999be4a2SHao Wu #define MFT_INBSEL      0x1c
76999be4a2SHao Wu 
77999be4a2SHao Wu #define MFT_MCTRL_ALL   0x64
78999be4a2SHao Wu #define MFT_ICLR_ALL    0x3f
79999be4a2SHao Wu #define MFT_IEN_ALL     0x3f
80999be4a2SHao Wu #define MFT_CPCFG_EQ_MODE 0x44
81999be4a2SHao Wu 
82999be4a2SHao Wu #define MFT_CKC_C2CSEL  BIT(3)
83999be4a2SHao Wu #define MFT_CKC_C1CSEL  BIT(0)
84999be4a2SHao Wu 
85999be4a2SHao Wu #define MFT_ICTRL_TFPND BIT(5)
86999be4a2SHao Wu #define MFT_ICTRL_TEPND BIT(4)
87999be4a2SHao Wu #define MFT_ICTRL_TDPND BIT(3)
88999be4a2SHao Wu #define MFT_ICTRL_TCPND BIT(2)
89999be4a2SHao Wu #define MFT_ICTRL_TBPND BIT(1)
90999be4a2SHao Wu #define MFT_ICTRL_TAPND BIT(0)
91999be4a2SHao Wu 
92999be4a2SHao Wu #define MFT_MAX_CNT     0xffff
93999be4a2SHao Wu #define MFT_TIMEOUT     0x5000
94999be4a2SHao Wu 
95999be4a2SHao Wu #define DEFAULT_RPM     19800
96999be4a2SHao Wu #define DEFAULT_PRSC    255
97999be4a2SHao Wu #define MFT_PULSE_PER_REVOLUTION 2
98999be4a2SHao Wu 
99999be4a2SHao Wu #define MAX_ERROR       1
100999be4a2SHao Wu 
10173314f13SHao Wu typedef struct PWMModule {
10273314f13SHao Wu     int irq;
10373314f13SHao Wu     uint64_t base_addr;
10473314f13SHao Wu } PWMModule;
10573314f13SHao Wu 
10673314f13SHao Wu typedef struct PWM {
10773314f13SHao Wu     uint32_t cnr_offset;
10873314f13SHao Wu     uint32_t cmr_offset;
10973314f13SHao Wu     uint32_t pdr_offset;
11073314f13SHao Wu     uint32_t pwdr_offset;
11173314f13SHao Wu } PWM;
11273314f13SHao Wu 
11373314f13SHao Wu typedef struct TestData {
11473314f13SHao Wu     const PWMModule *module;
11573314f13SHao Wu     const PWM *pwm;
11673314f13SHao Wu } TestData;
11773314f13SHao Wu 
11873314f13SHao Wu static const PWMModule pwm_module_list[] = {
11973314f13SHao Wu     {
12073314f13SHao Wu         .irq        = 93,
12173314f13SHao Wu         .base_addr  = 0xf0103000
12273314f13SHao Wu     },
12373314f13SHao Wu     {
12473314f13SHao Wu         .irq        = 94,
12573314f13SHao Wu         .base_addr  = 0xf0104000
12673314f13SHao Wu     }
12773314f13SHao Wu };
12873314f13SHao Wu 
12973314f13SHao Wu static const PWM pwm_list[] = {
13073314f13SHao Wu     {
13173314f13SHao Wu         .cnr_offset     = 0x0c,
13273314f13SHao Wu         .cmr_offset     = 0x10,
13373314f13SHao Wu         .pdr_offset     = 0x14,
13473314f13SHao Wu         .pwdr_offset    = 0x44,
13573314f13SHao Wu     },
13673314f13SHao Wu     {
13773314f13SHao Wu         .cnr_offset     = 0x18,
13873314f13SHao Wu         .cmr_offset     = 0x1c,
13973314f13SHao Wu         .pdr_offset     = 0x20,
14073314f13SHao Wu         .pwdr_offset    = 0x48,
14173314f13SHao Wu     },
14273314f13SHao Wu     {
14373314f13SHao Wu         .cnr_offset     = 0x24,
14473314f13SHao Wu         .cmr_offset     = 0x28,
14573314f13SHao Wu         .pdr_offset     = 0x2c,
14673314f13SHao Wu         .pwdr_offset    = 0x4c,
14773314f13SHao Wu     },
14873314f13SHao Wu     {
14973314f13SHao Wu         .cnr_offset     = 0x30,
15073314f13SHao Wu         .cmr_offset     = 0x34,
15173314f13SHao Wu         .pdr_offset     = 0x38,
15273314f13SHao Wu         .pwdr_offset    = 0x50,
15373314f13SHao Wu     },
15473314f13SHao Wu };
15573314f13SHao Wu 
15673314f13SHao Wu static const int ppr_base[] = { 0, 0, 8, 8 };
15773314f13SHao Wu static const int csr_base[] = { 0, 4, 8, 12 };
15873314f13SHao Wu static const int pcr_base[] = { 0, 8, 12, 16 };
15973314f13SHao Wu 
16073314f13SHao Wu static const uint32_t ppr_list[] = {
16173314f13SHao Wu     0,
16273314f13SHao Wu     1,
16373314f13SHao Wu     10,
16473314f13SHao Wu     100,
16573314f13SHao Wu     255, /* Max possible value. */
16673314f13SHao Wu };
16773314f13SHao Wu 
16873314f13SHao Wu static const uint32_t csr_list[] = {
16973314f13SHao Wu     0,
17073314f13SHao Wu     1,
17173314f13SHao Wu     2,
17273314f13SHao Wu     3,
17373314f13SHao Wu     4, /* Max possible value. */
17473314f13SHao Wu };
17573314f13SHao Wu 
17673314f13SHao Wu static const uint32_t cnr_list[] = {
17773314f13SHao Wu     0,
17873314f13SHao Wu     1,
17973314f13SHao Wu     50,
18073314f13SHao Wu     100,
18173314f13SHao Wu     150,
18273314f13SHao Wu     200,
18373314f13SHao Wu     1000,
18473314f13SHao Wu     10000,
18573314f13SHao Wu     65535, /* Max possible value. */
18673314f13SHao Wu };
18773314f13SHao Wu 
18873314f13SHao Wu static const uint32_t cmr_list[] = {
18973314f13SHao Wu     0,
19073314f13SHao Wu     1,
19173314f13SHao Wu     10,
19273314f13SHao Wu     50,
19373314f13SHao Wu     100,
19473314f13SHao Wu     150,
19573314f13SHao Wu     200,
19673314f13SHao Wu     1000,
19773314f13SHao Wu     10000,
19873314f13SHao Wu     65535, /* Max possible value. */
19973314f13SHao Wu };
20073314f13SHao Wu 
20173314f13SHao Wu /* Returns the index of the PWM module. */
pwm_module_index(const PWMModule * module)20273314f13SHao Wu static int pwm_module_index(const PWMModule *module)
20373314f13SHao Wu {
20473314f13SHao Wu     ptrdiff_t diff = module - pwm_module_list;
20573314f13SHao Wu 
20630258545SPeter Maydell     g_assert(diff >= 0 && diff < ARRAY_SIZE(pwm_module_list));
20773314f13SHao Wu 
20873314f13SHao Wu     return diff;
20973314f13SHao Wu }
21073314f13SHao Wu 
21173314f13SHao Wu /* Returns the index of the PWM entry. */
pwm_index(const PWM * pwm)21273314f13SHao Wu static int pwm_index(const PWM *pwm)
21373314f13SHao Wu {
21473314f13SHao Wu     ptrdiff_t diff = pwm - pwm_list;
21573314f13SHao Wu 
21630258545SPeter Maydell     g_assert(diff >= 0 && diff < ARRAY_SIZE(pwm_list));
21773314f13SHao Wu 
21873314f13SHao Wu     return diff;
21973314f13SHao Wu }
22073314f13SHao Wu 
pwm_qom_get(QTestState * qts,const char * path,const char * name)22173314f13SHao Wu static uint64_t pwm_qom_get(QTestState *qts, const char *path, const char *name)
22273314f13SHao Wu {
22373314f13SHao Wu     QDict *response;
2243e829c04SGan Qixin     uint64_t val;
22573314f13SHao Wu 
22677034bbcSPeter Maydell     if (verbosity_level >= 2) {
22773314f13SHao Wu         g_test_message("Getting properties %s from %s", name, path);
22877034bbcSPeter Maydell     }
22973314f13SHao Wu     response = qtest_qmp(qts, "{ 'execute': 'qom-get',"
23073314f13SHao Wu             " 'arguments': { 'path': %s, 'property': %s}}",
23173314f13SHao Wu             path, name);
23273314f13SHao Wu     /* The qom set message returns successfully. */
23373314f13SHao Wu     g_assert_true(qdict_haskey(response, "return"));
2343e829c04SGan Qixin     val = qnum_get_uint(qobject_to(QNum, qdict_get(response, "return")));
2353e829c04SGan Qixin     qobject_unref(response);
2363e829c04SGan Qixin     return val;
23773314f13SHao Wu }
23873314f13SHao Wu 
pwm_get_freq(QTestState * qts,int module_index,int pwm_index)23973314f13SHao Wu static uint64_t pwm_get_freq(QTestState *qts, int module_index, int pwm_index)
24073314f13SHao Wu {
24173314f13SHao Wu     char path[100];
24273314f13SHao Wu     char name[100];
24373314f13SHao Wu 
24473314f13SHao Wu     sprintf(path, "/machine/soc/pwm[%d]", module_index);
24573314f13SHao Wu     sprintf(name, "freq[%d]", pwm_index);
24673314f13SHao Wu 
24773314f13SHao Wu     return pwm_qom_get(qts, path, name);
24873314f13SHao Wu }
24973314f13SHao Wu 
pwm_get_duty(QTestState * qts,int module_index,int pwm_index)25073314f13SHao Wu static uint64_t pwm_get_duty(QTestState *qts, int module_index, int pwm_index)
25173314f13SHao Wu {
25273314f13SHao Wu     char path[100];
25373314f13SHao Wu     char name[100];
25473314f13SHao Wu 
25573314f13SHao Wu     sprintf(path, "/machine/soc/pwm[%d]", module_index);
25673314f13SHao Wu     sprintf(name, "duty[%d]", pwm_index);
25773314f13SHao Wu 
25873314f13SHao Wu     return pwm_qom_get(qts, path, name);
25973314f13SHao Wu }
26073314f13SHao Wu 
mft_qom_set(QTestState * qts,int index,const char * name,uint32_t value)261999be4a2SHao Wu static void mft_qom_set(QTestState *qts, int index, const char *name,
262999be4a2SHao Wu                         uint32_t value)
263999be4a2SHao Wu {
264999be4a2SHao Wu     QDict *response;
265999be4a2SHao Wu     char *path = g_strdup_printf("/machine/soc/mft[%d]", index);
266999be4a2SHao Wu 
26777034bbcSPeter Maydell     if (verbosity_level >= 2) {
268999be4a2SHao Wu         g_test_message("Setting properties %s of mft[%d] with value %u",
269999be4a2SHao Wu                        name, index, value);
27077034bbcSPeter Maydell     }
271999be4a2SHao Wu     response = qtest_qmp(qts, "{ 'execute': 'qom-set',"
272999be4a2SHao Wu             " 'arguments': { 'path': %s, "
273999be4a2SHao Wu             " 'property': %s, 'value': %u}}",
274999be4a2SHao Wu             path, name, value);
275999be4a2SHao Wu     /* The qom set message returns successfully. */
276999be4a2SHao Wu     g_assert_true(qdict_haskey(response, "return"));
277d412597eSMiaoqian Lin 
278d412597eSMiaoqian Lin     qobject_unref(response);
279d412597eSMiaoqian Lin     g_free(path);
280999be4a2SHao Wu }
281999be4a2SHao Wu 
get_pll(uint32_t con)28273314f13SHao Wu static uint32_t get_pll(uint32_t con)
28373314f13SHao Wu {
28473314f13SHao Wu     return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con)
28573314f13SHao Wu             * PLL_OTDV2(con));
28673314f13SHao Wu }
28773314f13SHao Wu 
read_pclk(QTestState * qts,bool mft)288999be4a2SHao Wu static uint64_t read_pclk(QTestState *qts, bool mft)
28973314f13SHao Wu {
29073314f13SHao Wu     uint64_t freq = REF_HZ;
29173314f13SHao Wu     uint32_t clksel = qtest_readl(qts, CLK_BA + CLKSEL);
29273314f13SHao Wu     uint32_t pllcon;
29373314f13SHao Wu     uint32_t clkdiv1 = qtest_readl(qts, CLK_BA + CLKDIV1);
29473314f13SHao Wu     uint32_t clkdiv2 = qtest_readl(qts, CLK_BA + CLKDIV2);
295999be4a2SHao Wu     uint32_t apbdiv = mft ? APB4CKDIV(clkdiv2) : APB3CKDIV(clkdiv2);
29673314f13SHao Wu 
29773314f13SHao Wu     switch (CPUCKSEL(clksel)) {
29873314f13SHao Wu     case 0:
29973314f13SHao Wu         pllcon = qtest_readl(qts, CLK_BA + PLLCON0);
30073314f13SHao Wu         freq = get_pll(pllcon);
30173314f13SHao Wu         break;
30273314f13SHao Wu     case 1:
30373314f13SHao Wu         pllcon = qtest_readl(qts, CLK_BA + PLLCON1);
30473314f13SHao Wu         freq = get_pll(pllcon);
30573314f13SHao Wu         break;
30673314f13SHao Wu     case 2:
30773314f13SHao Wu         break;
30873314f13SHao Wu     case 3:
30973314f13SHao Wu         break;
31073314f13SHao Wu     default:
31173314f13SHao Wu         g_assert_not_reached();
31273314f13SHao Wu     }
31373314f13SHao Wu 
314999be4a2SHao Wu     freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + apbdiv);
31573314f13SHao Wu 
31673314f13SHao Wu     return freq;
31773314f13SHao Wu }
31873314f13SHao Wu 
pwm_selector(uint32_t csr)31973314f13SHao Wu static uint32_t pwm_selector(uint32_t csr)
32073314f13SHao Wu {
32173314f13SHao Wu     switch (csr) {
32273314f13SHao Wu     case 0:
32373314f13SHao Wu         return 2;
32473314f13SHao Wu     case 1:
32573314f13SHao Wu         return 4;
32673314f13SHao Wu     case 2:
32773314f13SHao Wu         return 8;
32873314f13SHao Wu     case 3:
32973314f13SHao Wu         return 16;
33073314f13SHao Wu     case 4:
33173314f13SHao Wu         return 1;
33273314f13SHao Wu     default:
33373314f13SHao Wu         g_assert_not_reached();
33473314f13SHao Wu     }
33573314f13SHao Wu }
33673314f13SHao Wu 
pwm_compute_freq(QTestState * qts,uint32_t ppr,uint32_t csr,uint32_t cnr)33773314f13SHao Wu static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr,
33873314f13SHao Wu         uint32_t cnr)
33973314f13SHao Wu {
340999be4a2SHao Wu     return read_pclk(qts, false) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1));
34173314f13SHao Wu }
34273314f13SHao Wu 
pwm_compute_duty(uint32_t cnr,uint32_t cmr,bool inverted)34373314f13SHao Wu static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted)
34473314f13SHao Wu {
3451e5ce6e1SHao Wu     uint32_t duty;
34673314f13SHao Wu 
34773314f13SHao Wu     if (cnr == 0) {
34873314f13SHao Wu         /* PWM is stopped. */
34973314f13SHao Wu         duty = 0;
35073314f13SHao Wu     } else if (cmr >= cnr) {
35173314f13SHao Wu         duty = MAX_DUTY;
35273314f13SHao Wu     } else {
3531e5ce6e1SHao Wu         duty = (uint64_t)MAX_DUTY * (cmr + 1) / (cnr + 1);
35473314f13SHao Wu     }
35573314f13SHao Wu 
35673314f13SHao Wu     if (inverted) {
35773314f13SHao Wu         duty = MAX_DUTY - duty;
35873314f13SHao Wu     }
35973314f13SHao Wu 
36073314f13SHao Wu     return duty;
36173314f13SHao Wu }
36273314f13SHao Wu 
pwm_read(QTestState * qts,const TestData * td,unsigned offset)36373314f13SHao Wu static uint32_t pwm_read(QTestState *qts, const TestData *td, unsigned offset)
36473314f13SHao Wu {
36573314f13SHao Wu     return qtest_readl(qts, td->module->base_addr + offset);
36673314f13SHao Wu }
36773314f13SHao Wu 
pwm_write(QTestState * qts,const TestData * td,unsigned offset,uint32_t value)36873314f13SHao Wu static void pwm_write(QTestState *qts, const TestData *td, unsigned offset,
36973314f13SHao Wu         uint32_t value)
37073314f13SHao Wu {
37173314f13SHao Wu     qtest_writel(qts, td->module->base_addr + offset, value);
37273314f13SHao Wu }
37373314f13SHao Wu 
mft_readb(QTestState * qts,int index,unsigned offset)374999be4a2SHao Wu static uint8_t mft_readb(QTestState *qts, int index, unsigned offset)
375999be4a2SHao Wu {
376999be4a2SHao Wu     return qtest_readb(qts, MFT_BA(index) + offset);
377999be4a2SHao Wu }
378999be4a2SHao Wu 
mft_readw(QTestState * qts,int index,unsigned offset)379999be4a2SHao Wu static uint16_t mft_readw(QTestState *qts, int index, unsigned offset)
380999be4a2SHao Wu {
381999be4a2SHao Wu     return qtest_readw(qts, MFT_BA(index) + offset);
382999be4a2SHao Wu }
383999be4a2SHao Wu 
mft_writeb(QTestState * qts,int index,unsigned offset,uint8_t value)384999be4a2SHao Wu static void mft_writeb(QTestState *qts, int index, unsigned offset,
385999be4a2SHao Wu                         uint8_t value)
386999be4a2SHao Wu {
387999be4a2SHao Wu     qtest_writeb(qts, MFT_BA(index) + offset, value);
388999be4a2SHao Wu }
389999be4a2SHao Wu 
mft_writew(QTestState * qts,int index,unsigned offset,uint16_t value)390999be4a2SHao Wu static void mft_writew(QTestState *qts, int index, unsigned offset,
391999be4a2SHao Wu                         uint16_t value)
392999be4a2SHao Wu {
393999be4a2SHao Wu     return qtest_writew(qts, MFT_BA(index) + offset, value);
394999be4a2SHao Wu }
395999be4a2SHao Wu 
pwm_read_ppr(QTestState * qts,const TestData * td)39673314f13SHao Wu static uint32_t pwm_read_ppr(QTestState *qts, const TestData *td)
39773314f13SHao Wu {
39873314f13SHao Wu     return extract32(pwm_read(qts, td, PPR), ppr_base[pwm_index(td->pwm)], 8);
39973314f13SHao Wu }
40073314f13SHao Wu 
pwm_write_ppr(QTestState * qts,const TestData * td,uint32_t value)40173314f13SHao Wu static void pwm_write_ppr(QTestState *qts, const TestData *td, uint32_t value)
40273314f13SHao Wu {
40373314f13SHao Wu     pwm_write(qts, td, PPR, value << ppr_base[pwm_index(td->pwm)]);
40473314f13SHao Wu }
40573314f13SHao Wu 
pwm_read_csr(QTestState * qts,const TestData * td)40673314f13SHao Wu static uint32_t pwm_read_csr(QTestState *qts, const TestData *td)
40773314f13SHao Wu {
40873314f13SHao Wu     return extract32(pwm_read(qts, td, CSR), csr_base[pwm_index(td->pwm)], 3);
40973314f13SHao Wu }
41073314f13SHao Wu 
pwm_write_csr(QTestState * qts,const TestData * td,uint32_t value)41173314f13SHao Wu static void pwm_write_csr(QTestState *qts, const TestData *td, uint32_t value)
41273314f13SHao Wu {
41373314f13SHao Wu     pwm_write(qts, td, CSR, value << csr_base[pwm_index(td->pwm)]);
41473314f13SHao Wu }
41573314f13SHao Wu 
pwm_read_pcr(QTestState * qts,const TestData * td)41673314f13SHao Wu static uint32_t pwm_read_pcr(QTestState *qts, const TestData *td)
41773314f13SHao Wu {
41873314f13SHao Wu     return extract32(pwm_read(qts, td, PCR), pcr_base[pwm_index(td->pwm)], 4);
41973314f13SHao Wu }
42073314f13SHao Wu 
pwm_write_pcr(QTestState * qts,const TestData * td,uint32_t value)42173314f13SHao Wu static void pwm_write_pcr(QTestState *qts, const TestData *td, uint32_t value)
42273314f13SHao Wu {
42373314f13SHao Wu     pwm_write(qts, td, PCR, value << pcr_base[pwm_index(td->pwm)]);
42473314f13SHao Wu }
42573314f13SHao Wu 
pwm_read_cnr(QTestState * qts,const TestData * td)42673314f13SHao Wu static uint32_t pwm_read_cnr(QTestState *qts, const TestData *td)
42773314f13SHao Wu {
42873314f13SHao Wu     return pwm_read(qts, td, td->pwm->cnr_offset);
42973314f13SHao Wu }
43073314f13SHao Wu 
pwm_write_cnr(QTestState * qts,const TestData * td,uint32_t value)43173314f13SHao Wu static void pwm_write_cnr(QTestState *qts, const TestData *td, uint32_t value)
43273314f13SHao Wu {
43373314f13SHao Wu     pwm_write(qts, td, td->pwm->cnr_offset, value);
43473314f13SHao Wu }
43573314f13SHao Wu 
pwm_read_cmr(QTestState * qts,const TestData * td)43673314f13SHao Wu static uint32_t pwm_read_cmr(QTestState *qts, const TestData *td)
43773314f13SHao Wu {
43873314f13SHao Wu     return pwm_read(qts, td, td->pwm->cmr_offset);
43973314f13SHao Wu }
44073314f13SHao Wu 
pwm_write_cmr(QTestState * qts,const TestData * td,uint32_t value)44173314f13SHao Wu static void pwm_write_cmr(QTestState *qts, const TestData *td, uint32_t value)
44273314f13SHao Wu {
44373314f13SHao Wu     pwm_write(qts, td, td->pwm->cmr_offset, value);
44473314f13SHao Wu }
44573314f13SHao Wu 
mft_compute_index(const TestData * td)446999be4a2SHao Wu static int mft_compute_index(const TestData *td)
447999be4a2SHao Wu {
448999be4a2SHao Wu     int index = pwm_module_index(td->module) * ARRAY_SIZE(pwm_list) +
449999be4a2SHao Wu                 pwm_index(td->pwm);
450999be4a2SHao Wu 
451999be4a2SHao Wu     g_assert_cmpint(index, <,
452999be4a2SHao Wu                     ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list));
453999be4a2SHao Wu 
454999be4a2SHao Wu     return index;
455999be4a2SHao Wu }
456999be4a2SHao Wu 
mft_reset_counters(QTestState * qts,int index)457999be4a2SHao Wu static void mft_reset_counters(QTestState *qts, int index)
458999be4a2SHao Wu {
459999be4a2SHao Wu     mft_writew(qts, index, MFT_CNT1, MFT_MAX_CNT);
460999be4a2SHao Wu     mft_writew(qts, index, MFT_CNT2, MFT_MAX_CNT);
461999be4a2SHao Wu     mft_writew(qts, index, MFT_CRA, MFT_MAX_CNT);
462999be4a2SHao Wu     mft_writew(qts, index, MFT_CRB, MFT_MAX_CNT);
463999be4a2SHao Wu     mft_writew(qts, index, MFT_CPA, MFT_MAX_CNT - MFT_TIMEOUT);
464999be4a2SHao Wu     mft_writew(qts, index, MFT_CPB, MFT_MAX_CNT - MFT_TIMEOUT);
465999be4a2SHao Wu }
466999be4a2SHao Wu 
mft_init(QTestState * qts,const TestData * td)467999be4a2SHao Wu static void mft_init(QTestState *qts, const TestData *td)
468999be4a2SHao Wu {
469999be4a2SHao Wu     int index = mft_compute_index(td);
470999be4a2SHao Wu 
471999be4a2SHao Wu     /* Enable everything */
472999be4a2SHao Wu     mft_writeb(qts, index, MFT_CKC, 0);
473999be4a2SHao Wu     mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL);
474999be4a2SHao Wu     mft_writeb(qts, index, MFT_MCTRL, MFT_MCTRL_ALL);
475999be4a2SHao Wu     mft_writeb(qts, index, MFT_IEN, MFT_IEN_ALL);
476999be4a2SHao Wu     mft_writeb(qts, index, MFT_INASEL, 0);
477999be4a2SHao Wu     mft_writeb(qts, index, MFT_INBSEL, 0);
478999be4a2SHao Wu 
479999be4a2SHao Wu     /* Set cpcfg to use EQ mode, same as kernel driver */
480999be4a2SHao Wu     mft_writeb(qts, index, MFT_CPCFG, MFT_CPCFG_EQ_MODE);
481999be4a2SHao Wu 
482999be4a2SHao Wu     /* Write default counters, timeout and prescaler */
483999be4a2SHao Wu     mft_reset_counters(qts, index);
484999be4a2SHao Wu     mft_writeb(qts, index, MFT_PRSC, DEFAULT_PRSC);
485999be4a2SHao Wu 
486999be4a2SHao Wu     /* Write default max rpm via QMP */
487999be4a2SHao Wu     mft_qom_set(qts, index, "max_rpm[0]", DEFAULT_RPM);
488999be4a2SHao Wu     mft_qom_set(qts, index, "max_rpm[1]", DEFAULT_RPM);
489999be4a2SHao Wu }
490999be4a2SHao Wu 
mft_compute_cnt(uint32_t rpm,uint64_t clk)491999be4a2SHao Wu static int32_t mft_compute_cnt(uint32_t rpm, uint64_t clk)
492999be4a2SHao Wu {
493999be4a2SHao Wu     uint64_t cnt;
494999be4a2SHao Wu 
495999be4a2SHao Wu     if (rpm == 0) {
496999be4a2SHao Wu         return -1;
497999be4a2SHao Wu     }
498999be4a2SHao Wu 
499999be4a2SHao Wu     cnt = clk * 60 / ((DEFAULT_PRSC + 1) * rpm * MFT_PULSE_PER_REVOLUTION);
500999be4a2SHao Wu     if (cnt >= MFT_TIMEOUT) {
501999be4a2SHao Wu         return -1;
502999be4a2SHao Wu     }
503999be4a2SHao Wu     return MFT_MAX_CNT - cnt;
504999be4a2SHao Wu }
505999be4a2SHao Wu 
mft_verify_rpm(QTestState * qts,const TestData * td,uint64_t duty)506999be4a2SHao Wu static void mft_verify_rpm(QTestState *qts, const TestData *td, uint64_t duty)
507999be4a2SHao Wu {
508999be4a2SHao Wu     int index = mft_compute_index(td);
509999be4a2SHao Wu     uint16_t cnt, cr;
510999be4a2SHao Wu     uint32_t rpm = DEFAULT_RPM * duty / MAX_DUTY;
511999be4a2SHao Wu     uint64_t clk = read_pclk(qts, true);
512999be4a2SHao Wu     int32_t expected_cnt = mft_compute_cnt(rpm, clk);
513999be4a2SHao Wu 
514999be4a2SHao Wu     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
51577034bbcSPeter Maydell     if (verbosity_level >= 2) {
516999be4a2SHao Wu         g_test_message(
51777034bbcSPeter Maydell             "verifying rpm for mft[%d]: clk: %" PRIu64 ", duty: %" PRIu64
51877034bbcSPeter Maydell             ", rpm: %u, cnt: %d",
519999be4a2SHao Wu             index, clk, duty, rpm, expected_cnt);
52077034bbcSPeter Maydell     }
521999be4a2SHao Wu 
522999be4a2SHao Wu     /* Verify rpm for fan A */
523999be4a2SHao Wu     /* Stop capture */
524999be4a2SHao Wu     mft_writeb(qts, index, MFT_CKC, 0);
525999be4a2SHao Wu     mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL);
526999be4a2SHao Wu     mft_reset_counters(qts, index);
527999be4a2SHao Wu     g_assert_cmphex(mft_readw(qts, index, MFT_CNT1), ==, MFT_MAX_CNT);
528999be4a2SHao Wu     g_assert_cmphex(mft_readw(qts, index, MFT_CRA), ==, MFT_MAX_CNT);
529999be4a2SHao Wu     g_assert_cmphex(mft_readw(qts, index, MFT_CPA), ==,
530999be4a2SHao Wu                     MFT_MAX_CNT - MFT_TIMEOUT);
531999be4a2SHao Wu     /* Start capture */
532999be4a2SHao Wu     mft_writeb(qts, index, MFT_CKC, MFT_CKC_C1CSEL);
533999be4a2SHao Wu     g_assert_true(qtest_get_irq(qts, MFT_IRQ(index)));
534999be4a2SHao Wu     if (expected_cnt == -1) {
535999be4a2SHao Wu         g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TEPND);
536999be4a2SHao Wu     } else {
537999be4a2SHao Wu         g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TAPND);
538999be4a2SHao Wu         cnt = mft_readw(qts, index, MFT_CNT1);
539999be4a2SHao Wu         /*
540999be4a2SHao Wu          * Due to error in clock measurement and rounding, we might have a small
541999be4a2SHao Wu          * error in measuring RPM.
542999be4a2SHao Wu          */
543999be4a2SHao Wu         g_assert_cmphex(cnt + MAX_ERROR, >=, expected_cnt);
544999be4a2SHao Wu         g_assert_cmphex(cnt, <=, expected_cnt + MAX_ERROR);
545999be4a2SHao Wu         cr = mft_readw(qts, index, MFT_CRA);
546999be4a2SHao Wu         g_assert_cmphex(cnt, ==, cr);
547999be4a2SHao Wu     }
548999be4a2SHao Wu 
549999be4a2SHao Wu     /* Verify rpm for fan B */
550999be4a2SHao Wu 
551999be4a2SHao Wu     qtest_irq_intercept_out(qts, "/machine/soc/a9mpcore/gic");
552999be4a2SHao Wu }
553999be4a2SHao Wu 
55473314f13SHao Wu /* Check pwm registers can be reset to default value */
test_init(gconstpointer test_data)55573314f13SHao Wu static void test_init(gconstpointer test_data)
55673314f13SHao Wu {
55773314f13SHao Wu     const TestData *td = test_data;
558999be4a2SHao Wu     QTestState *qts = qtest_init("-machine npcm750-evb");
55973314f13SHao Wu     int module = pwm_module_index(td->module);
56073314f13SHao Wu     int pwm = pwm_index(td->pwm);
56173314f13SHao Wu 
56273314f13SHao Wu     g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0);
56373314f13SHao Wu     g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0);
56473314f13SHao Wu 
56573314f13SHao Wu     qtest_quit(qts);
56673314f13SHao Wu }
56773314f13SHao Wu 
56873314f13SHao Wu /* One-shot mode should not change frequency and duty cycle. */
test_oneshot(gconstpointer test_data)56973314f13SHao Wu static void test_oneshot(gconstpointer test_data)
57073314f13SHao Wu {
57173314f13SHao Wu     const TestData *td = test_data;
572999be4a2SHao Wu     QTestState *qts = qtest_init("-machine npcm750-evb");
57373314f13SHao Wu     int module = pwm_module_index(td->module);
57473314f13SHao Wu     int pwm = pwm_index(td->pwm);
57573314f13SHao Wu     uint32_t ppr, csr, pcr;
57673314f13SHao Wu     int i, j;
57773314f13SHao Wu 
57873314f13SHao Wu     pcr = CH_EN;
57973314f13SHao Wu     for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) {
58073314f13SHao Wu         ppr = ppr_list[i];
58173314f13SHao Wu         pwm_write_ppr(qts, td, ppr);
58273314f13SHao Wu 
58373314f13SHao Wu         for (j = 0; j < ARRAY_SIZE(csr_list); ++j) {
58473314f13SHao Wu             csr = csr_list[j];
58573314f13SHao Wu             pwm_write_csr(qts, td, csr);
58673314f13SHao Wu             pwm_write_pcr(qts, td, pcr);
58773314f13SHao Wu 
58873314f13SHao Wu             g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr);
58973314f13SHao Wu             g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr);
59073314f13SHao Wu             g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr);
59173314f13SHao Wu             g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0);
59273314f13SHao Wu             g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0);
59373314f13SHao Wu         }
59473314f13SHao Wu     }
59573314f13SHao Wu 
59673314f13SHao Wu     qtest_quit(qts);
59773314f13SHao Wu }
59873314f13SHao Wu 
59973314f13SHao Wu /* In toggle mode, the PWM generates correct outputs. */
test_toggle(gconstpointer test_data)60073314f13SHao Wu static void test_toggle(gconstpointer test_data)
60173314f13SHao Wu {
60273314f13SHao Wu     const TestData *td = test_data;
603999be4a2SHao Wu     QTestState *qts = qtest_init("-machine npcm750-evb");
60473314f13SHao Wu     int module = pwm_module_index(td->module);
60573314f13SHao Wu     int pwm = pwm_index(td->pwm);
60673314f13SHao Wu     uint32_t ppr, csr, pcr, cnr, cmr;
60773314f13SHao Wu     int i, j, k, l;
60873314f13SHao Wu     uint64_t expected_freq, expected_duty;
60971dc6ca2SThomas Huth     int cnr_step = g_test_quick() ? 2 : 1;
61073314f13SHao Wu 
611999be4a2SHao Wu     mft_init(qts, td);
612999be4a2SHao Wu 
61373314f13SHao Wu     pcr = CH_EN | CH_MOD;
61473314f13SHao Wu     for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) {
61573314f13SHao Wu         ppr = ppr_list[i];
61673314f13SHao Wu         pwm_write_ppr(qts, td, ppr);
61773314f13SHao Wu 
61873314f13SHao Wu         for (j = 0; j < ARRAY_SIZE(csr_list); ++j) {
61973314f13SHao Wu             csr = csr_list[j];
62073314f13SHao Wu             pwm_write_csr(qts, td, csr);
62173314f13SHao Wu 
62271dc6ca2SThomas Huth             for (k = 0; k < ARRAY_SIZE(cnr_list); k += cnr_step) {
62373314f13SHao Wu                 cnr = cnr_list[k];
62473314f13SHao Wu                 pwm_write_cnr(qts, td, cnr);
62573314f13SHao Wu 
62673314f13SHao Wu                 for (l = 0; l < ARRAY_SIZE(cmr_list); ++l) {
62773314f13SHao Wu                     cmr = cmr_list[l];
62873314f13SHao Wu                     pwm_write_cmr(qts, td, cmr);
62973314f13SHao Wu                     expected_freq = pwm_compute_freq(qts, ppr, csr, cnr);
63073314f13SHao Wu                     expected_duty = pwm_compute_duty(cnr, cmr, false);
63173314f13SHao Wu 
63273314f13SHao Wu                     pwm_write_pcr(qts, td, pcr);
63373314f13SHao Wu                     g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr);
63473314f13SHao Wu                     g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr);
63573314f13SHao Wu                     g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr);
63673314f13SHao Wu                     g_assert_cmpuint(pwm_read_cnr(qts, td), ==, cnr);
63773314f13SHao Wu                     g_assert_cmpuint(pwm_read_cmr(qts, td), ==, cmr);
63873314f13SHao Wu                     g_assert_cmpuint(pwm_get_duty(qts, module, pwm),
63973314f13SHao Wu                             ==, expected_duty);
64073314f13SHao Wu                     if (expected_duty != 0 && expected_duty != 100) {
64173314f13SHao Wu                         /* Duty cycle with 0 or 100 doesn't need frequency. */
64273314f13SHao Wu                         g_assert_cmpuint(pwm_get_freq(qts, module, pwm),
64373314f13SHao Wu                                 ==, expected_freq);
64473314f13SHao Wu                     }
64573314f13SHao Wu 
646999be4a2SHao Wu                     /* Test MFT's RPM is correct. */
647999be4a2SHao Wu                     mft_verify_rpm(qts, td, expected_duty);
648999be4a2SHao Wu 
64973314f13SHao Wu                     /* Test inverted mode */
65073314f13SHao Wu                     expected_duty = pwm_compute_duty(cnr, cmr, true);
65173314f13SHao Wu                     pwm_write_pcr(qts, td, pcr | CH_INV);
65273314f13SHao Wu                     g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr | CH_INV);
65373314f13SHao Wu                     g_assert_cmpuint(pwm_get_duty(qts, module, pwm),
65473314f13SHao Wu                             ==, expected_duty);
65573314f13SHao Wu                     if (expected_duty != 0 && expected_duty != 100) {
65673314f13SHao Wu                         /* Duty cycle with 0 or 100 doesn't need frequency. */
65773314f13SHao Wu                         g_assert_cmpuint(pwm_get_freq(qts, module, pwm),
65873314f13SHao Wu                                 ==, expected_freq);
65973314f13SHao Wu                     }
66073314f13SHao Wu 
66173314f13SHao Wu                 }
66273314f13SHao Wu             }
66373314f13SHao Wu         }
66473314f13SHao Wu     }
66573314f13SHao Wu 
66673314f13SHao Wu     qtest_quit(qts);
66773314f13SHao Wu }
66873314f13SHao Wu 
pwm_add_test(const char * name,const TestData * td,GTestDataFunc fn)66973314f13SHao Wu static void pwm_add_test(const char *name, const TestData* td,
67073314f13SHao Wu         GTestDataFunc fn)
67173314f13SHao Wu {
67273314f13SHao Wu     g_autofree char *full_name = g_strdup_printf(
67373314f13SHao Wu             "npcm7xx_pwm/module[%d]/pwm[%d]/%s", pwm_module_index(td->module),
67473314f13SHao Wu             pwm_index(td->pwm), name);
67573314f13SHao Wu     qtest_add_data_func(full_name, td, fn);
67673314f13SHao Wu }
67773314f13SHao Wu #define add_test(name, td) pwm_add_test(#name, td, test_##name)
67873314f13SHao Wu 
main(int argc,char ** argv)67973314f13SHao Wu int main(int argc, char **argv)
68073314f13SHao Wu {
68173314f13SHao Wu     TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)];
68271dc6ca2SThomas Huth     int pwm_module_list_cnt = 1, pwm_list_cnt = 1;
68373314f13SHao Wu 
68477034bbcSPeter Maydell     char *v_env = getenv("V");
68577034bbcSPeter Maydell 
68677034bbcSPeter Maydell     if (v_env) {
68777034bbcSPeter Maydell         verbosity_level = atoi(v_env);
68877034bbcSPeter Maydell     }
68977034bbcSPeter Maydell 
69073314f13SHao Wu     g_test_init(&argc, &argv, NULL);
69173314f13SHao Wu 
69271dc6ca2SThomas Huth     if (!g_test_quick()) {
69371dc6ca2SThomas Huth         pwm_module_list_cnt = ARRAY_SIZE(pwm_module_list);
69471dc6ca2SThomas Huth         pwm_list_cnt = ARRAY_SIZE(pwm_list);
69571dc6ca2SThomas Huth     }
69671dc6ca2SThomas Huth 
69771dc6ca2SThomas Huth     for (int i = 0; i < pwm_module_list_cnt; ++i) {
69871dc6ca2SThomas Huth         for (int j = 0; j < pwm_list_cnt; ++j) {
69973314f13SHao Wu             TestData *td = &test_data_list[i * ARRAY_SIZE(pwm_list) + j];
70073314f13SHao Wu 
70173314f13SHao Wu             td->module = &pwm_module_list[i];
70273314f13SHao Wu             td->pwm = &pwm_list[j];
70373314f13SHao Wu 
70473314f13SHao Wu             add_test(init, td);
70573314f13SHao Wu             add_test(oneshot, td);
70673314f13SHao Wu             add_test(toggle, td);
70773314f13SHao Wu         }
70873314f13SHao Wu     }
70973314f13SHao Wu 
71073314f13SHao Wu     return g_test_run();
71173314f13SHao Wu }
712