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