xref: /qemu/tests/qtest/npcm7xx_pwm-test.c (revision d412597ec5a8406b2af6aa5fb7740e77c1bd3f8c)
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"
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 
20430258545SPeter 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 
21430258545SPeter 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"));
271*d412597eSMiaoqian Lin 
272*d412597eSMiaoqian Lin     qobject_unref(response);
273*d412597eSMiaoqian Lin     g_free(path);
274999be4a2SHao Wu }
275999be4a2SHao Wu 
27673314f13SHao Wu static uint32_t get_pll(uint32_t con)
27773314f13SHao Wu {
27873314f13SHao Wu     return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con)
27973314f13SHao Wu             * PLL_OTDV2(con));
28073314f13SHao Wu }
28173314f13SHao Wu 
282999be4a2SHao Wu static uint64_t read_pclk(QTestState *qts, bool mft)
28373314f13SHao Wu {
28473314f13SHao Wu     uint64_t freq = REF_HZ;
28573314f13SHao Wu     uint32_t clksel = qtest_readl(qts, CLK_BA + CLKSEL);
28673314f13SHao Wu     uint32_t pllcon;
28773314f13SHao Wu     uint32_t clkdiv1 = qtest_readl(qts, CLK_BA + CLKDIV1);
28873314f13SHao Wu     uint32_t clkdiv2 = qtest_readl(qts, CLK_BA + CLKDIV2);
289999be4a2SHao Wu     uint32_t apbdiv = mft ? APB4CKDIV(clkdiv2) : APB3CKDIV(clkdiv2);
29073314f13SHao Wu 
29173314f13SHao Wu     switch (CPUCKSEL(clksel)) {
29273314f13SHao Wu     case 0:
29373314f13SHao Wu         pllcon = qtest_readl(qts, CLK_BA + PLLCON0);
29473314f13SHao Wu         freq = get_pll(pllcon);
29573314f13SHao Wu         break;
29673314f13SHao Wu     case 1:
29773314f13SHao Wu         pllcon = qtest_readl(qts, CLK_BA + PLLCON1);
29873314f13SHao Wu         freq = get_pll(pllcon);
29973314f13SHao Wu         break;
30073314f13SHao Wu     case 2:
30173314f13SHao Wu         break;
30273314f13SHao Wu     case 3:
30373314f13SHao Wu         break;
30473314f13SHao Wu     default:
30573314f13SHao Wu         g_assert_not_reached();
30673314f13SHao Wu     }
30773314f13SHao Wu 
308999be4a2SHao Wu     freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + apbdiv);
30973314f13SHao Wu 
31073314f13SHao Wu     return freq;
31173314f13SHao Wu }
31273314f13SHao Wu 
31373314f13SHao Wu static uint32_t pwm_selector(uint32_t csr)
31473314f13SHao Wu {
31573314f13SHao Wu     switch (csr) {
31673314f13SHao Wu     case 0:
31773314f13SHao Wu         return 2;
31873314f13SHao Wu     case 1:
31973314f13SHao Wu         return 4;
32073314f13SHao Wu     case 2:
32173314f13SHao Wu         return 8;
32273314f13SHao Wu     case 3:
32373314f13SHao Wu         return 16;
32473314f13SHao Wu     case 4:
32573314f13SHao Wu         return 1;
32673314f13SHao Wu     default:
32773314f13SHao Wu         g_assert_not_reached();
32873314f13SHao Wu     }
32973314f13SHao Wu }
33073314f13SHao Wu 
33173314f13SHao Wu static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr,
33273314f13SHao Wu         uint32_t cnr)
33373314f13SHao Wu {
334999be4a2SHao Wu     return read_pclk(qts, false) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1));
33573314f13SHao Wu }
33673314f13SHao Wu 
33773314f13SHao Wu static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted)
33873314f13SHao Wu {
3391e5ce6e1SHao Wu     uint32_t duty;
34073314f13SHao Wu 
34173314f13SHao Wu     if (cnr == 0) {
34273314f13SHao Wu         /* PWM is stopped. */
34373314f13SHao Wu         duty = 0;
34473314f13SHao Wu     } else if (cmr >= cnr) {
34573314f13SHao Wu         duty = MAX_DUTY;
34673314f13SHao Wu     } else {
3471e5ce6e1SHao Wu         duty = (uint64_t)MAX_DUTY * (cmr + 1) / (cnr + 1);
34873314f13SHao Wu     }
34973314f13SHao Wu 
35073314f13SHao Wu     if (inverted) {
35173314f13SHao Wu         duty = MAX_DUTY - duty;
35273314f13SHao Wu     }
35373314f13SHao Wu 
35473314f13SHao Wu     return duty;
35573314f13SHao Wu }
35673314f13SHao Wu 
35773314f13SHao Wu static uint32_t pwm_read(QTestState *qts, const TestData *td, unsigned offset)
35873314f13SHao Wu {
35973314f13SHao Wu     return qtest_readl(qts, td->module->base_addr + offset);
36073314f13SHao Wu }
36173314f13SHao Wu 
36273314f13SHao Wu static void pwm_write(QTestState *qts, const TestData *td, unsigned offset,
36373314f13SHao Wu         uint32_t value)
36473314f13SHao Wu {
36573314f13SHao Wu     qtest_writel(qts, td->module->base_addr + offset, value);
36673314f13SHao Wu }
36773314f13SHao Wu 
368999be4a2SHao Wu static uint8_t mft_readb(QTestState *qts, int index, unsigned offset)
369999be4a2SHao Wu {
370999be4a2SHao Wu     return qtest_readb(qts, MFT_BA(index) + offset);
371999be4a2SHao Wu }
372999be4a2SHao Wu 
373999be4a2SHao Wu static uint16_t mft_readw(QTestState *qts, int index, unsigned offset)
374999be4a2SHao Wu {
375999be4a2SHao Wu     return qtest_readw(qts, MFT_BA(index) + offset);
376999be4a2SHao Wu }
377999be4a2SHao Wu 
378999be4a2SHao Wu static void mft_writeb(QTestState *qts, int index, unsigned offset,
379999be4a2SHao Wu                         uint8_t value)
380999be4a2SHao Wu {
381999be4a2SHao Wu     qtest_writeb(qts, MFT_BA(index) + offset, value);
382999be4a2SHao Wu }
383999be4a2SHao Wu 
384999be4a2SHao Wu static void mft_writew(QTestState *qts, int index, unsigned offset,
385999be4a2SHao Wu                         uint16_t value)
386999be4a2SHao Wu {
387999be4a2SHao Wu     return qtest_writew(qts, MFT_BA(index) + offset, value);
388999be4a2SHao Wu }
389999be4a2SHao Wu 
39073314f13SHao Wu static uint32_t pwm_read_ppr(QTestState *qts, const TestData *td)
39173314f13SHao Wu {
39273314f13SHao Wu     return extract32(pwm_read(qts, td, PPR), ppr_base[pwm_index(td->pwm)], 8);
39373314f13SHao Wu }
39473314f13SHao Wu 
39573314f13SHao Wu static void pwm_write_ppr(QTestState *qts, const TestData *td, uint32_t value)
39673314f13SHao Wu {
39773314f13SHao Wu     pwm_write(qts, td, PPR, value << ppr_base[pwm_index(td->pwm)]);
39873314f13SHao Wu }
39973314f13SHao Wu 
40073314f13SHao Wu static uint32_t pwm_read_csr(QTestState *qts, const TestData *td)
40173314f13SHao Wu {
40273314f13SHao Wu     return extract32(pwm_read(qts, td, CSR), csr_base[pwm_index(td->pwm)], 3);
40373314f13SHao Wu }
40473314f13SHao Wu 
40573314f13SHao Wu static void pwm_write_csr(QTestState *qts, const TestData *td, uint32_t value)
40673314f13SHao Wu {
40773314f13SHao Wu     pwm_write(qts, td, CSR, value << csr_base[pwm_index(td->pwm)]);
40873314f13SHao Wu }
40973314f13SHao Wu 
41073314f13SHao Wu static uint32_t pwm_read_pcr(QTestState *qts, const TestData *td)
41173314f13SHao Wu {
41273314f13SHao Wu     return extract32(pwm_read(qts, td, PCR), pcr_base[pwm_index(td->pwm)], 4);
41373314f13SHao Wu }
41473314f13SHao Wu 
41573314f13SHao Wu static void pwm_write_pcr(QTestState *qts, const TestData *td, uint32_t value)
41673314f13SHao Wu {
41773314f13SHao Wu     pwm_write(qts, td, PCR, value << pcr_base[pwm_index(td->pwm)]);
41873314f13SHao Wu }
41973314f13SHao Wu 
42073314f13SHao Wu static uint32_t pwm_read_cnr(QTestState *qts, const TestData *td)
42173314f13SHao Wu {
42273314f13SHao Wu     return pwm_read(qts, td, td->pwm->cnr_offset);
42373314f13SHao Wu }
42473314f13SHao Wu 
42573314f13SHao Wu static void pwm_write_cnr(QTestState *qts, const TestData *td, uint32_t value)
42673314f13SHao Wu {
42773314f13SHao Wu     pwm_write(qts, td, td->pwm->cnr_offset, value);
42873314f13SHao Wu }
42973314f13SHao Wu 
43073314f13SHao Wu static uint32_t pwm_read_cmr(QTestState *qts, const TestData *td)
43173314f13SHao Wu {
43273314f13SHao Wu     return pwm_read(qts, td, td->pwm->cmr_offset);
43373314f13SHao Wu }
43473314f13SHao Wu 
43573314f13SHao Wu static void pwm_write_cmr(QTestState *qts, const TestData *td, uint32_t value)
43673314f13SHao Wu {
43773314f13SHao Wu     pwm_write(qts, td, td->pwm->cmr_offset, value);
43873314f13SHao Wu }
43973314f13SHao Wu 
440999be4a2SHao Wu static int mft_compute_index(const TestData *td)
441999be4a2SHao Wu {
442999be4a2SHao Wu     int index = pwm_module_index(td->module) * ARRAY_SIZE(pwm_list) +
443999be4a2SHao Wu                 pwm_index(td->pwm);
444999be4a2SHao Wu 
445999be4a2SHao Wu     g_assert_cmpint(index, <,
446999be4a2SHao Wu                     ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list));
447999be4a2SHao Wu 
448999be4a2SHao Wu     return index;
449999be4a2SHao Wu }
450999be4a2SHao Wu 
451999be4a2SHao Wu static void mft_reset_counters(QTestState *qts, int index)
452999be4a2SHao Wu {
453999be4a2SHao Wu     mft_writew(qts, index, MFT_CNT1, MFT_MAX_CNT);
454999be4a2SHao Wu     mft_writew(qts, index, MFT_CNT2, MFT_MAX_CNT);
455999be4a2SHao Wu     mft_writew(qts, index, MFT_CRA, MFT_MAX_CNT);
456999be4a2SHao Wu     mft_writew(qts, index, MFT_CRB, MFT_MAX_CNT);
457999be4a2SHao Wu     mft_writew(qts, index, MFT_CPA, MFT_MAX_CNT - MFT_TIMEOUT);
458999be4a2SHao Wu     mft_writew(qts, index, MFT_CPB, MFT_MAX_CNT - MFT_TIMEOUT);
459999be4a2SHao Wu }
460999be4a2SHao Wu 
461999be4a2SHao Wu static void mft_init(QTestState *qts, const TestData *td)
462999be4a2SHao Wu {
463999be4a2SHao Wu     int index = mft_compute_index(td);
464999be4a2SHao Wu 
465999be4a2SHao Wu     /* Enable everything */
466999be4a2SHao Wu     mft_writeb(qts, index, MFT_CKC, 0);
467999be4a2SHao Wu     mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL);
468999be4a2SHao Wu     mft_writeb(qts, index, MFT_MCTRL, MFT_MCTRL_ALL);
469999be4a2SHao Wu     mft_writeb(qts, index, MFT_IEN, MFT_IEN_ALL);
470999be4a2SHao Wu     mft_writeb(qts, index, MFT_INASEL, 0);
471999be4a2SHao Wu     mft_writeb(qts, index, MFT_INBSEL, 0);
472999be4a2SHao Wu 
473999be4a2SHao Wu     /* Set cpcfg to use EQ mode, same as kernel driver */
474999be4a2SHao Wu     mft_writeb(qts, index, MFT_CPCFG, MFT_CPCFG_EQ_MODE);
475999be4a2SHao Wu 
476999be4a2SHao Wu     /* Write default counters, timeout and prescaler */
477999be4a2SHao Wu     mft_reset_counters(qts, index);
478999be4a2SHao Wu     mft_writeb(qts, index, MFT_PRSC, DEFAULT_PRSC);
479999be4a2SHao Wu 
480999be4a2SHao Wu     /* Write default max rpm via QMP */
481999be4a2SHao Wu     mft_qom_set(qts, index, "max_rpm[0]", DEFAULT_RPM);
482999be4a2SHao Wu     mft_qom_set(qts, index, "max_rpm[1]", DEFAULT_RPM);
483999be4a2SHao Wu }
484999be4a2SHao Wu 
485999be4a2SHao Wu static int32_t mft_compute_cnt(uint32_t rpm, uint64_t clk)
486999be4a2SHao Wu {
487999be4a2SHao Wu     uint64_t cnt;
488999be4a2SHao Wu 
489999be4a2SHao Wu     if (rpm == 0) {
490999be4a2SHao Wu         return -1;
491999be4a2SHao Wu     }
492999be4a2SHao Wu 
493999be4a2SHao Wu     cnt = clk * 60 / ((DEFAULT_PRSC + 1) * rpm * MFT_PULSE_PER_REVOLUTION);
494999be4a2SHao Wu     if (cnt >= MFT_TIMEOUT) {
495999be4a2SHao Wu         return -1;
496999be4a2SHao Wu     }
497999be4a2SHao Wu     return MFT_MAX_CNT - cnt;
498999be4a2SHao Wu }
499999be4a2SHao Wu 
500999be4a2SHao Wu static void mft_verify_rpm(QTestState *qts, const TestData *td, uint64_t duty)
501999be4a2SHao Wu {
502999be4a2SHao Wu     int index = mft_compute_index(td);
503999be4a2SHao Wu     uint16_t cnt, cr;
504999be4a2SHao Wu     uint32_t rpm = DEFAULT_RPM * duty / MAX_DUTY;
505999be4a2SHao Wu     uint64_t clk = read_pclk(qts, true);
506999be4a2SHao Wu     int32_t expected_cnt = mft_compute_cnt(rpm, clk);
507999be4a2SHao Wu 
508999be4a2SHao Wu     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
509999be4a2SHao Wu     g_test_message(
510999be4a2SHao Wu         "verifying rpm for mft[%d]: clk: %" PRIu64 ", duty: %" PRIu64 ", rpm: %u, cnt: %d",
511999be4a2SHao Wu         index, clk, duty, rpm, expected_cnt);
512999be4a2SHao Wu 
513999be4a2SHao Wu     /* Verify rpm for fan A */
514999be4a2SHao Wu     /* Stop capture */
515999be4a2SHao Wu     mft_writeb(qts, index, MFT_CKC, 0);
516999be4a2SHao Wu     mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL);
517999be4a2SHao Wu     mft_reset_counters(qts, index);
518999be4a2SHao Wu     g_assert_cmphex(mft_readw(qts, index, MFT_CNT1), ==, MFT_MAX_CNT);
519999be4a2SHao Wu     g_assert_cmphex(mft_readw(qts, index, MFT_CRA), ==, MFT_MAX_CNT);
520999be4a2SHao Wu     g_assert_cmphex(mft_readw(qts, index, MFT_CPA), ==,
521999be4a2SHao Wu                     MFT_MAX_CNT - MFT_TIMEOUT);
522999be4a2SHao Wu     /* Start capture */
523999be4a2SHao Wu     mft_writeb(qts, index, MFT_CKC, MFT_CKC_C1CSEL);
524999be4a2SHao Wu     g_assert_true(qtest_get_irq(qts, MFT_IRQ(index)));
525999be4a2SHao Wu     if (expected_cnt == -1) {
526999be4a2SHao Wu         g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TEPND);
527999be4a2SHao Wu     } else {
528999be4a2SHao Wu         g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TAPND);
529999be4a2SHao Wu         cnt = mft_readw(qts, index, MFT_CNT1);
530999be4a2SHao Wu         /*
531999be4a2SHao Wu          * Due to error in clock measurement and rounding, we might have a small
532999be4a2SHao Wu          * error in measuring RPM.
533999be4a2SHao Wu          */
534999be4a2SHao Wu         g_assert_cmphex(cnt + MAX_ERROR, >=, expected_cnt);
535999be4a2SHao Wu         g_assert_cmphex(cnt, <=, expected_cnt + MAX_ERROR);
536999be4a2SHao Wu         cr = mft_readw(qts, index, MFT_CRA);
537999be4a2SHao Wu         g_assert_cmphex(cnt, ==, cr);
538999be4a2SHao Wu     }
539999be4a2SHao Wu 
540999be4a2SHao Wu     /* Verify rpm for fan B */
541999be4a2SHao Wu 
542999be4a2SHao Wu     qtest_irq_intercept_out(qts, "/machine/soc/a9mpcore/gic");
543999be4a2SHao Wu }
544999be4a2SHao Wu 
54573314f13SHao Wu /* Check pwm registers can be reset to default value */
54673314f13SHao Wu static void test_init(gconstpointer test_data)
54773314f13SHao Wu {
54873314f13SHao Wu     const TestData *td = test_data;
549999be4a2SHao Wu     QTestState *qts = qtest_init("-machine npcm750-evb");
55073314f13SHao Wu     int module = pwm_module_index(td->module);
55173314f13SHao Wu     int pwm = pwm_index(td->pwm);
55273314f13SHao Wu 
55373314f13SHao Wu     g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0);
55473314f13SHao Wu     g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0);
55573314f13SHao Wu 
55673314f13SHao Wu     qtest_quit(qts);
55773314f13SHao Wu }
55873314f13SHao Wu 
55973314f13SHao Wu /* One-shot mode should not change frequency and duty cycle. */
56073314f13SHao Wu static void test_oneshot(gconstpointer test_data)
56173314f13SHao Wu {
56273314f13SHao Wu     const TestData *td = test_data;
563999be4a2SHao Wu     QTestState *qts = qtest_init("-machine npcm750-evb");
56473314f13SHao Wu     int module = pwm_module_index(td->module);
56573314f13SHao Wu     int pwm = pwm_index(td->pwm);
56673314f13SHao Wu     uint32_t ppr, csr, pcr;
56773314f13SHao Wu     int i, j;
56873314f13SHao Wu 
56973314f13SHao Wu     pcr = CH_EN;
57073314f13SHao Wu     for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) {
57173314f13SHao Wu         ppr = ppr_list[i];
57273314f13SHao Wu         pwm_write_ppr(qts, td, ppr);
57373314f13SHao Wu 
57473314f13SHao Wu         for (j = 0; j < ARRAY_SIZE(csr_list); ++j) {
57573314f13SHao Wu             csr = csr_list[j];
57673314f13SHao Wu             pwm_write_csr(qts, td, csr);
57773314f13SHao Wu             pwm_write_pcr(qts, td, pcr);
57873314f13SHao Wu 
57973314f13SHao Wu             g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr);
58073314f13SHao Wu             g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr);
58173314f13SHao Wu             g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr);
58273314f13SHao Wu             g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0);
58373314f13SHao Wu             g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0);
58473314f13SHao Wu         }
58573314f13SHao Wu     }
58673314f13SHao Wu 
58773314f13SHao Wu     qtest_quit(qts);
58873314f13SHao Wu }
58973314f13SHao Wu 
59073314f13SHao Wu /* In toggle mode, the PWM generates correct outputs. */
59173314f13SHao Wu static void test_toggle(gconstpointer test_data)
59273314f13SHao Wu {
59373314f13SHao Wu     const TestData *td = test_data;
594999be4a2SHao Wu     QTestState *qts = qtest_init("-machine npcm750-evb");
59573314f13SHao Wu     int module = pwm_module_index(td->module);
59673314f13SHao Wu     int pwm = pwm_index(td->pwm);
59773314f13SHao Wu     uint32_t ppr, csr, pcr, cnr, cmr;
59873314f13SHao Wu     int i, j, k, l;
59973314f13SHao Wu     uint64_t expected_freq, expected_duty;
60073314f13SHao Wu 
601999be4a2SHao Wu     mft_init(qts, td);
602999be4a2SHao Wu 
60373314f13SHao Wu     pcr = CH_EN | CH_MOD;
60473314f13SHao Wu     for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) {
60573314f13SHao Wu         ppr = ppr_list[i];
60673314f13SHao Wu         pwm_write_ppr(qts, td, ppr);
60773314f13SHao Wu 
60873314f13SHao Wu         for (j = 0; j < ARRAY_SIZE(csr_list); ++j) {
60973314f13SHao Wu             csr = csr_list[j];
61073314f13SHao Wu             pwm_write_csr(qts, td, csr);
61173314f13SHao Wu 
61273314f13SHao Wu             for (k = 0; k < ARRAY_SIZE(cnr_list); ++k) {
61373314f13SHao Wu                 cnr = cnr_list[k];
61473314f13SHao Wu                 pwm_write_cnr(qts, td, cnr);
61573314f13SHao Wu 
61673314f13SHao Wu                 for (l = 0; l < ARRAY_SIZE(cmr_list); ++l) {
61773314f13SHao Wu                     cmr = cmr_list[l];
61873314f13SHao Wu                     pwm_write_cmr(qts, td, cmr);
61973314f13SHao Wu                     expected_freq = pwm_compute_freq(qts, ppr, csr, cnr);
62073314f13SHao Wu                     expected_duty = pwm_compute_duty(cnr, cmr, false);
62173314f13SHao Wu 
62273314f13SHao Wu                     pwm_write_pcr(qts, td, pcr);
62373314f13SHao Wu                     g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr);
62473314f13SHao Wu                     g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr);
62573314f13SHao Wu                     g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr);
62673314f13SHao Wu                     g_assert_cmpuint(pwm_read_cnr(qts, td), ==, cnr);
62773314f13SHao Wu                     g_assert_cmpuint(pwm_read_cmr(qts, td), ==, cmr);
62873314f13SHao Wu                     g_assert_cmpuint(pwm_get_duty(qts, module, pwm),
62973314f13SHao Wu                             ==, expected_duty);
63073314f13SHao Wu                     if (expected_duty != 0 && expected_duty != 100) {
63173314f13SHao Wu                         /* Duty cycle with 0 or 100 doesn't need frequency. */
63273314f13SHao Wu                         g_assert_cmpuint(pwm_get_freq(qts, module, pwm),
63373314f13SHao Wu                                 ==, expected_freq);
63473314f13SHao Wu                     }
63573314f13SHao Wu 
636999be4a2SHao Wu                     /* Test MFT's RPM is correct. */
637999be4a2SHao Wu                     mft_verify_rpm(qts, td, expected_duty);
638999be4a2SHao Wu 
63973314f13SHao Wu                     /* Test inverted mode */
64073314f13SHao Wu                     expected_duty = pwm_compute_duty(cnr, cmr, true);
64173314f13SHao Wu                     pwm_write_pcr(qts, td, pcr | CH_INV);
64273314f13SHao Wu                     g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr | CH_INV);
64373314f13SHao Wu                     g_assert_cmpuint(pwm_get_duty(qts, module, pwm),
64473314f13SHao Wu                             ==, expected_duty);
64573314f13SHao Wu                     if (expected_duty != 0 && expected_duty != 100) {
64673314f13SHao Wu                         /* Duty cycle with 0 or 100 doesn't need frequency. */
64773314f13SHao Wu                         g_assert_cmpuint(pwm_get_freq(qts, module, pwm),
64873314f13SHao Wu                                 ==, expected_freq);
64973314f13SHao Wu                     }
65073314f13SHao Wu 
65173314f13SHao Wu                 }
65273314f13SHao Wu             }
65373314f13SHao Wu         }
65473314f13SHao Wu     }
65573314f13SHao Wu 
65673314f13SHao Wu     qtest_quit(qts);
65773314f13SHao Wu }
65873314f13SHao Wu 
65973314f13SHao Wu static void pwm_add_test(const char *name, const TestData* td,
66073314f13SHao Wu         GTestDataFunc fn)
66173314f13SHao Wu {
66273314f13SHao Wu     g_autofree char *full_name = g_strdup_printf(
66373314f13SHao Wu             "npcm7xx_pwm/module[%d]/pwm[%d]/%s", pwm_module_index(td->module),
66473314f13SHao Wu             pwm_index(td->pwm), name);
66573314f13SHao Wu     qtest_add_data_func(full_name, td, fn);
66673314f13SHao Wu }
66773314f13SHao Wu #define add_test(name, td) pwm_add_test(#name, td, test_##name)
66873314f13SHao Wu 
66973314f13SHao Wu int main(int argc, char **argv)
67073314f13SHao Wu {
67173314f13SHao Wu     TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)];
67273314f13SHao Wu 
67373314f13SHao Wu     g_test_init(&argc, &argv, NULL);
67473314f13SHao Wu 
67573314f13SHao Wu     for (int i = 0; i < ARRAY_SIZE(pwm_module_list); ++i) {
67673314f13SHao Wu         for (int j = 0; j < ARRAY_SIZE(pwm_list); ++j) {
67773314f13SHao Wu             TestData *td = &test_data_list[i * ARRAY_SIZE(pwm_list) + j];
67873314f13SHao Wu 
67973314f13SHao Wu             td->module = &pwm_module_list[i];
68073314f13SHao Wu             td->pwm = &pwm_list[j];
68173314f13SHao Wu 
68273314f13SHao Wu             add_test(init, td);
68373314f13SHao Wu             add_test(oneshot, td);
68473314f13SHao Wu             add_test(toggle, td);
68573314f13SHao Wu         }
68673314f13SHao Wu     }
68773314f13SHao Wu 
68873314f13SHao Wu     return g_test_run();
68973314f13SHao Wu }
690