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