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 23*77034bbcSPeter Maydell static int verbosity_level; 24*77034bbcSPeter 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. */ 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. */ 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 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 226*77034bbcSPeter Maydell if (verbosity_level >= 2) { 22773314f13SHao Wu g_test_message("Getting properties %s from %s", name, path); 228*77034bbcSPeter 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 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 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 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 267*77034bbcSPeter Maydell if (verbosity_level >= 2) { 268999be4a2SHao Wu g_test_message("Setting properties %s of mft[%d] with value %u", 269999be4a2SHao Wu name, index, value); 270*77034bbcSPeter 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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"); 515*77034bbcSPeter Maydell if (verbosity_level >= 2) { 516999be4a2SHao Wu g_test_message( 517*77034bbcSPeter Maydell "verifying rpm for mft[%d]: clk: %" PRIu64 ", duty: %" PRIu64 518*77034bbcSPeter Maydell ", rpm: %u, cnt: %d", 519999be4a2SHao Wu index, clk, duty, rpm, expected_cnt); 520*77034bbcSPeter 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 */ 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. */ 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. */ 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; 60973314f13SHao Wu 610999be4a2SHao Wu mft_init(qts, td); 611999be4a2SHao Wu 61273314f13SHao Wu pcr = CH_EN | CH_MOD; 61373314f13SHao Wu for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) { 61473314f13SHao Wu ppr = ppr_list[i]; 61573314f13SHao Wu pwm_write_ppr(qts, td, ppr); 61673314f13SHao Wu 61773314f13SHao Wu for (j = 0; j < ARRAY_SIZE(csr_list); ++j) { 61873314f13SHao Wu csr = csr_list[j]; 61973314f13SHao Wu pwm_write_csr(qts, td, csr); 62073314f13SHao Wu 62173314f13SHao Wu for (k = 0; k < ARRAY_SIZE(cnr_list); ++k) { 62273314f13SHao Wu cnr = cnr_list[k]; 62373314f13SHao Wu pwm_write_cnr(qts, td, cnr); 62473314f13SHao Wu 62573314f13SHao Wu for (l = 0; l < ARRAY_SIZE(cmr_list); ++l) { 62673314f13SHao Wu cmr = cmr_list[l]; 62773314f13SHao Wu pwm_write_cmr(qts, td, cmr); 62873314f13SHao Wu expected_freq = pwm_compute_freq(qts, ppr, csr, cnr); 62973314f13SHao Wu expected_duty = pwm_compute_duty(cnr, cmr, false); 63073314f13SHao Wu 63173314f13SHao Wu pwm_write_pcr(qts, td, pcr); 63273314f13SHao Wu g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr); 63373314f13SHao Wu g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr); 63473314f13SHao Wu g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr); 63573314f13SHao Wu g_assert_cmpuint(pwm_read_cnr(qts, td), ==, cnr); 63673314f13SHao Wu g_assert_cmpuint(pwm_read_cmr(qts, td), ==, cmr); 63773314f13SHao Wu g_assert_cmpuint(pwm_get_duty(qts, module, pwm), 63873314f13SHao Wu ==, expected_duty); 63973314f13SHao Wu if (expected_duty != 0 && expected_duty != 100) { 64073314f13SHao Wu /* Duty cycle with 0 or 100 doesn't need frequency. */ 64173314f13SHao Wu g_assert_cmpuint(pwm_get_freq(qts, module, pwm), 64273314f13SHao Wu ==, expected_freq); 64373314f13SHao Wu } 64473314f13SHao Wu 645999be4a2SHao Wu /* Test MFT's RPM is correct. */ 646999be4a2SHao Wu mft_verify_rpm(qts, td, expected_duty); 647999be4a2SHao Wu 64873314f13SHao Wu /* Test inverted mode */ 64973314f13SHao Wu expected_duty = pwm_compute_duty(cnr, cmr, true); 65073314f13SHao Wu pwm_write_pcr(qts, td, pcr | CH_INV); 65173314f13SHao Wu g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr | CH_INV); 65273314f13SHao Wu g_assert_cmpuint(pwm_get_duty(qts, module, pwm), 65373314f13SHao Wu ==, expected_duty); 65473314f13SHao Wu if (expected_duty != 0 && expected_duty != 100) { 65573314f13SHao Wu /* Duty cycle with 0 or 100 doesn't need frequency. */ 65673314f13SHao Wu g_assert_cmpuint(pwm_get_freq(qts, module, pwm), 65773314f13SHao Wu ==, expected_freq); 65873314f13SHao Wu } 65973314f13SHao Wu 66073314f13SHao Wu } 66173314f13SHao Wu } 66273314f13SHao Wu } 66373314f13SHao Wu } 66473314f13SHao Wu 66573314f13SHao Wu qtest_quit(qts); 66673314f13SHao Wu } 66773314f13SHao Wu 66873314f13SHao Wu static void pwm_add_test(const char *name, const TestData* td, 66973314f13SHao Wu GTestDataFunc fn) 67073314f13SHao Wu { 67173314f13SHao Wu g_autofree char *full_name = g_strdup_printf( 67273314f13SHao Wu "npcm7xx_pwm/module[%d]/pwm[%d]/%s", pwm_module_index(td->module), 67373314f13SHao Wu pwm_index(td->pwm), name); 67473314f13SHao Wu qtest_add_data_func(full_name, td, fn); 67573314f13SHao Wu } 67673314f13SHao Wu #define add_test(name, td) pwm_add_test(#name, td, test_##name) 67773314f13SHao Wu 67873314f13SHao Wu int main(int argc, char **argv) 67973314f13SHao Wu { 68073314f13SHao Wu TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)]; 68173314f13SHao Wu 682*77034bbcSPeter Maydell char *v_env = getenv("V"); 683*77034bbcSPeter Maydell 684*77034bbcSPeter Maydell if (v_env) { 685*77034bbcSPeter Maydell verbosity_level = atoi(v_env); 686*77034bbcSPeter Maydell } 687*77034bbcSPeter Maydell 68873314f13SHao Wu g_test_init(&argc, &argv, NULL); 68973314f13SHao Wu 69073314f13SHao Wu for (int i = 0; i < ARRAY_SIZE(pwm_module_list); ++i) { 69173314f13SHao Wu for (int j = 0; j < ARRAY_SIZE(pwm_list); ++j) { 69273314f13SHao Wu TestData *td = &test_data_list[i * ARRAY_SIZE(pwm_list) + j]; 69373314f13SHao Wu 69473314f13SHao Wu td->module = &pwm_module_list[i]; 69573314f13SHao Wu td->pwm = &pwm_list[j]; 69673314f13SHao Wu 69773314f13SHao Wu add_test(init, td); 69873314f13SHao Wu add_test(oneshot, td); 69973314f13SHao Wu add_test(toggle, td); 70073314f13SHao Wu } 70173314f13SHao Wu } 70273314f13SHao Wu 70373314f13SHao Wu return g_test_run(); 70473314f13SHao Wu } 705