1*380a37e4SHao Wu /* 2*380a37e4SHao Wu * Nuvoton NPCM7xx MFT Module 3*380a37e4SHao Wu * 4*380a37e4SHao Wu * Copyright 2021 Google LLC 5*380a37e4SHao Wu * 6*380a37e4SHao Wu * This program is free software; you can redistribute it and/or modify it 7*380a37e4SHao Wu * under the terms of the GNU General Public License as published by the 8*380a37e4SHao Wu * Free Software Foundation; either version 2 of the License, or 9*380a37e4SHao Wu * (at your option) any later version. 10*380a37e4SHao Wu * 11*380a37e4SHao Wu * This program is distributed in the hope that it will be useful, but WITHOUT 12*380a37e4SHao Wu * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13*380a37e4SHao Wu * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14*380a37e4SHao Wu * for more details. 15*380a37e4SHao Wu */ 16*380a37e4SHao Wu 17*380a37e4SHao Wu #include "qemu/osdep.h" 18*380a37e4SHao Wu #include "hw/irq.h" 19*380a37e4SHao Wu #include "hw/qdev-clock.h" 20*380a37e4SHao Wu #include "hw/qdev-properties.h" 21*380a37e4SHao Wu #include "hw/misc/npcm7xx_mft.h" 22*380a37e4SHao Wu #include "hw/misc/npcm7xx_pwm.h" 23*380a37e4SHao Wu #include "hw/registerfields.h" 24*380a37e4SHao Wu #include "migration/vmstate.h" 25*380a37e4SHao Wu #include "qapi/error.h" 26*380a37e4SHao Wu #include "qapi/visitor.h" 27*380a37e4SHao Wu #include "qemu/bitops.h" 28*380a37e4SHao Wu #include "qemu/error-report.h" 29*380a37e4SHao Wu #include "qemu/log.h" 30*380a37e4SHao Wu #include "qemu/module.h" 31*380a37e4SHao Wu #include "qemu/timer.h" 32*380a37e4SHao Wu #include "qemu/units.h" 33*380a37e4SHao Wu #include "trace.h" 34*380a37e4SHao Wu 35*380a37e4SHao Wu /* 36*380a37e4SHao Wu * Some of the registers can only accessed via 16-bit ops and some can only 37*380a37e4SHao Wu * be accessed via 8-bit ops. However we mark all of them using REG16 to 38*380a37e4SHao Wu * simplify implementation. npcm7xx_mft_check_mem_op checks the access length 39*380a37e4SHao Wu * of memory operations. 40*380a37e4SHao Wu */ 41*380a37e4SHao Wu REG16(NPCM7XX_MFT_CNT1, 0x00); 42*380a37e4SHao Wu REG16(NPCM7XX_MFT_CRA, 0x02); 43*380a37e4SHao Wu REG16(NPCM7XX_MFT_CRB, 0x04); 44*380a37e4SHao Wu REG16(NPCM7XX_MFT_CNT2, 0x06); 45*380a37e4SHao Wu REG16(NPCM7XX_MFT_PRSC, 0x08); 46*380a37e4SHao Wu REG16(NPCM7XX_MFT_CKC, 0x0a); 47*380a37e4SHao Wu REG16(NPCM7XX_MFT_MCTRL, 0x0c); 48*380a37e4SHao Wu REG16(NPCM7XX_MFT_ICTRL, 0x0e); 49*380a37e4SHao Wu REG16(NPCM7XX_MFT_ICLR, 0x10); 50*380a37e4SHao Wu REG16(NPCM7XX_MFT_IEN, 0x12); 51*380a37e4SHao Wu REG16(NPCM7XX_MFT_CPA, 0x14); 52*380a37e4SHao Wu REG16(NPCM7XX_MFT_CPB, 0x16); 53*380a37e4SHao Wu REG16(NPCM7XX_MFT_CPCFG, 0x18); 54*380a37e4SHao Wu REG16(NPCM7XX_MFT_INASEL, 0x1a); 55*380a37e4SHao Wu REG16(NPCM7XX_MFT_INBSEL, 0x1c); 56*380a37e4SHao Wu 57*380a37e4SHao Wu /* Register Fields */ 58*380a37e4SHao Wu #define NPCM7XX_MFT_CKC_C2CSEL BIT(3) 59*380a37e4SHao Wu #define NPCM7XX_MFT_CKC_C1CSEL BIT(0) 60*380a37e4SHao Wu 61*380a37e4SHao Wu #define NPCM7XX_MFT_MCTRL_TBEN BIT(6) 62*380a37e4SHao Wu #define NPCM7XX_MFT_MCTRL_TAEN BIT(5) 63*380a37e4SHao Wu #define NPCM7XX_MFT_MCTRL_TBEDG BIT(4) 64*380a37e4SHao Wu #define NPCM7XX_MFT_MCTRL_TAEDG BIT(3) 65*380a37e4SHao Wu #define NPCM7XX_MFT_MCTRL_MODE5 BIT(2) 66*380a37e4SHao Wu 67*380a37e4SHao Wu #define NPCM7XX_MFT_ICTRL_TFPND BIT(5) 68*380a37e4SHao Wu #define NPCM7XX_MFT_ICTRL_TEPND BIT(4) 69*380a37e4SHao Wu #define NPCM7XX_MFT_ICTRL_TDPND BIT(3) 70*380a37e4SHao Wu #define NPCM7XX_MFT_ICTRL_TCPND BIT(2) 71*380a37e4SHao Wu #define NPCM7XX_MFT_ICTRL_TBPND BIT(1) 72*380a37e4SHao Wu #define NPCM7XX_MFT_ICTRL_TAPND BIT(0) 73*380a37e4SHao Wu 74*380a37e4SHao Wu #define NPCM7XX_MFT_ICLR_TFCLR BIT(5) 75*380a37e4SHao Wu #define NPCM7XX_MFT_ICLR_TECLR BIT(4) 76*380a37e4SHao Wu #define NPCM7XX_MFT_ICLR_TDCLR BIT(3) 77*380a37e4SHao Wu #define NPCM7XX_MFT_ICLR_TCCLR BIT(2) 78*380a37e4SHao Wu #define NPCM7XX_MFT_ICLR_TBCLR BIT(1) 79*380a37e4SHao Wu #define NPCM7XX_MFT_ICLR_TACLR BIT(0) 80*380a37e4SHao Wu 81*380a37e4SHao Wu #define NPCM7XX_MFT_IEN_TFIEN BIT(5) 82*380a37e4SHao Wu #define NPCM7XX_MFT_IEN_TEIEN BIT(4) 83*380a37e4SHao Wu #define NPCM7XX_MFT_IEN_TDIEN BIT(3) 84*380a37e4SHao Wu #define NPCM7XX_MFT_IEN_TCIEN BIT(2) 85*380a37e4SHao Wu #define NPCM7XX_MFT_IEN_TBIEN BIT(1) 86*380a37e4SHao Wu #define NPCM7XX_MFT_IEN_TAIEN BIT(0) 87*380a37e4SHao Wu 88*380a37e4SHao Wu #define NPCM7XX_MFT_CPCFG_GET_B(rv) extract8((rv), 4, 4) 89*380a37e4SHao Wu #define NPCM7XX_MFT_CPCFG_GET_A(rv) extract8((rv), 0, 4) 90*380a37e4SHao Wu #define NPCM7XX_MFT_CPCFG_HIEN BIT(3) 91*380a37e4SHao Wu #define NPCM7XX_MFT_CPCFG_EQEN BIT(2) 92*380a37e4SHao Wu #define NPCM7XX_MFT_CPCFG_LOEN BIT(1) 93*380a37e4SHao Wu #define NPCM7XX_MFT_CPCFG_CPSEL BIT(0) 94*380a37e4SHao Wu 95*380a37e4SHao Wu #define NPCM7XX_MFT_INASEL_SELA BIT(0) 96*380a37e4SHao Wu #define NPCM7XX_MFT_INBSEL_SELB BIT(0) 97*380a37e4SHao Wu 98*380a37e4SHao Wu /* Max CNT values of the module. The CNT value is a countdown from it. */ 99*380a37e4SHao Wu #define NPCM7XX_MFT_MAX_CNT 0xFFFF 100*380a37e4SHao Wu 101*380a37e4SHao Wu /* Each fan revolution should generated 2 pulses */ 102*380a37e4SHao Wu #define NPCM7XX_MFT_PULSE_PER_REVOLUTION 2 103*380a37e4SHao Wu 104*380a37e4SHao Wu typedef enum NPCM7xxMFTCaptureState { 105*380a37e4SHao Wu /* capture succeeded with a valid CNT value. */ 106*380a37e4SHao Wu NPCM7XX_CAPTURE_SUCCEED, 107*380a37e4SHao Wu /* capture stopped prematurely due to reaching CPCFG condition. */ 108*380a37e4SHao Wu NPCM7XX_CAPTURE_COMPARE_HIT, 109*380a37e4SHao Wu /* capture fails since it reaches underflow condition for CNT. */ 110*380a37e4SHao Wu NPCM7XX_CAPTURE_UNDERFLOW, 111*380a37e4SHao Wu } NPCM7xxMFTCaptureState; 112*380a37e4SHao Wu 113*380a37e4SHao Wu static void npcm7xx_mft_reset(NPCM7xxMFTState *s) 114*380a37e4SHao Wu { 115*380a37e4SHao Wu int i; 116*380a37e4SHao Wu 117*380a37e4SHao Wu /* Only registers PRSC ~ INBSEL need to be reset. */ 118*380a37e4SHao Wu for (i = R_NPCM7XX_MFT_PRSC; i <= R_NPCM7XX_MFT_INBSEL; ++i) { 119*380a37e4SHao Wu s->regs[i] = 0; 120*380a37e4SHao Wu } 121*380a37e4SHao Wu } 122*380a37e4SHao Wu 123*380a37e4SHao Wu static void npcm7xx_mft_clear_interrupt(NPCM7xxMFTState *s, uint8_t iclr) 124*380a37e4SHao Wu { 125*380a37e4SHao Wu /* 126*380a37e4SHao Wu * Clear bits in ICTRL where corresponding bits in iclr is 1. 127*380a37e4SHao Wu * Both iclr and ictrl are 8-bit regs. (See npcm7xx_mft_check_mem_op) 128*380a37e4SHao Wu */ 129*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] &= ~iclr; 130*380a37e4SHao Wu } 131*380a37e4SHao Wu 132*380a37e4SHao Wu /* 133*380a37e4SHao Wu * If the CPCFG's condition should be triggered during count down from 134*380a37e4SHao Wu * NPCM7XX_MFT_MAX_CNT to src if compared to tgt, return the count when 135*380a37e4SHao Wu * the condition is triggered. 136*380a37e4SHao Wu * Otherwise return -1. 137*380a37e4SHao Wu * Since tgt is uint16_t it must always <= NPCM7XX_MFT_MAX_CNT. 138*380a37e4SHao Wu */ 139*380a37e4SHao Wu static int npcm7xx_mft_compare(int32_t src, uint16_t tgt, uint8_t cpcfg) 140*380a37e4SHao Wu { 141*380a37e4SHao Wu if (cpcfg & NPCM7XX_MFT_CPCFG_HIEN) { 142*380a37e4SHao Wu return NPCM7XX_MFT_MAX_CNT; 143*380a37e4SHao Wu } 144*380a37e4SHao Wu if ((cpcfg & NPCM7XX_MFT_CPCFG_EQEN) && (src <= tgt)) { 145*380a37e4SHao Wu return tgt; 146*380a37e4SHao Wu } 147*380a37e4SHao Wu if ((cpcfg & NPCM7XX_MFT_CPCFG_LOEN) && (tgt > 0) && (src < tgt)) { 148*380a37e4SHao Wu return tgt - 1; 149*380a37e4SHao Wu } 150*380a37e4SHao Wu 151*380a37e4SHao Wu return -1; 152*380a37e4SHao Wu } 153*380a37e4SHao Wu 154*380a37e4SHao Wu /* Compute CNT according to corresponding fan's RPM. */ 155*380a37e4SHao Wu static NPCM7xxMFTCaptureState npcm7xx_mft_compute_cnt( 156*380a37e4SHao Wu Clock *clock, uint32_t max_rpm, uint32_t duty, uint16_t tgt, 157*380a37e4SHao Wu uint8_t cpcfg, uint16_t *cnt) 158*380a37e4SHao Wu { 159*380a37e4SHao Wu uint32_t rpm = (uint64_t)max_rpm * (uint64_t)duty / NPCM7XX_PWM_MAX_DUTY; 160*380a37e4SHao Wu int32_t count; 161*380a37e4SHao Wu int stopped; 162*380a37e4SHao Wu NPCM7xxMFTCaptureState state; 163*380a37e4SHao Wu 164*380a37e4SHao Wu if (rpm == 0) { 165*380a37e4SHao Wu /* 166*380a37e4SHao Wu * If RPM = 0, capture won't happen. CNT will continue count down. 167*380a37e4SHao Wu * So it's effective equivalent to have a cnt > NPCM7XX_MFT_MAX_CNT 168*380a37e4SHao Wu */ 169*380a37e4SHao Wu count = NPCM7XX_MFT_MAX_CNT + 1; 170*380a37e4SHao Wu } else { 171*380a37e4SHao Wu /* 172*380a37e4SHao Wu * RPM = revolution/min. The time for one revlution (in ns) is 173*380a37e4SHao Wu * MINUTE_TO_NANOSECOND / RPM. 174*380a37e4SHao Wu */ 175*380a37e4SHao Wu count = clock_ns_to_ticks(clock, (60 * NANOSECONDS_PER_SECOND) / 176*380a37e4SHao Wu (rpm * NPCM7XX_MFT_PULSE_PER_REVOLUTION)); 177*380a37e4SHao Wu } 178*380a37e4SHao Wu 179*380a37e4SHao Wu if (count > NPCM7XX_MFT_MAX_CNT) { 180*380a37e4SHao Wu count = -1; 181*380a37e4SHao Wu } else { 182*380a37e4SHao Wu /* The CNT is a countdown value from NPCM7XX_MFT_MAX_CNT. */ 183*380a37e4SHao Wu count = NPCM7XX_MFT_MAX_CNT - count; 184*380a37e4SHao Wu } 185*380a37e4SHao Wu stopped = npcm7xx_mft_compare(count, tgt, cpcfg); 186*380a37e4SHao Wu if (stopped == -1) { 187*380a37e4SHao Wu if (count == -1) { 188*380a37e4SHao Wu /* Underflow */ 189*380a37e4SHao Wu state = NPCM7XX_CAPTURE_UNDERFLOW; 190*380a37e4SHao Wu } else { 191*380a37e4SHao Wu state = NPCM7XX_CAPTURE_SUCCEED; 192*380a37e4SHao Wu } 193*380a37e4SHao Wu } else { 194*380a37e4SHao Wu count = stopped; 195*380a37e4SHao Wu state = NPCM7XX_CAPTURE_COMPARE_HIT; 196*380a37e4SHao Wu } 197*380a37e4SHao Wu 198*380a37e4SHao Wu if (count != -1) { 199*380a37e4SHao Wu *cnt = count; 200*380a37e4SHao Wu } 201*380a37e4SHao Wu trace_npcm7xx_mft_rpm(clock->canonical_path, clock_get_hz(clock), 202*380a37e4SHao Wu state, count, rpm, duty); 203*380a37e4SHao Wu return state; 204*380a37e4SHao Wu } 205*380a37e4SHao Wu 206*380a37e4SHao Wu /* 207*380a37e4SHao Wu * Capture Fan RPM and update CNT and CR registers accordingly. 208*380a37e4SHao Wu * Raise IRQ if certain contidions are met in IEN. 209*380a37e4SHao Wu */ 210*380a37e4SHao Wu static void npcm7xx_mft_capture(NPCM7xxMFTState *s) 211*380a37e4SHao Wu { 212*380a37e4SHao Wu int irq_level = 0; 213*380a37e4SHao Wu NPCM7xxMFTCaptureState state; 214*380a37e4SHao Wu int sel; 215*380a37e4SHao Wu uint8_t cpcfg; 216*380a37e4SHao Wu 217*380a37e4SHao Wu /* 218*380a37e4SHao Wu * If not mode 5, the behavior is undefined. We just do nothing in this 219*380a37e4SHao Wu * case. 220*380a37e4SHao Wu */ 221*380a37e4SHao Wu if (!(s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_MODE5)) { 222*380a37e4SHao Wu return; 223*380a37e4SHao Wu } 224*380a37e4SHao Wu 225*380a37e4SHao Wu /* Capture input A. */ 226*380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TAEN && 227*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) { 228*380a37e4SHao Wu sel = s->regs[R_NPCM7XX_MFT_INASEL] & NPCM7XX_MFT_INASEL_SELA; 229*380a37e4SHao Wu cpcfg = NPCM7XX_MFT_CPCFG_GET_A(s->regs[R_NPCM7XX_MFT_CPCFG]); 230*380a37e4SHao Wu state = npcm7xx_mft_compute_cnt(s->clock_1, 231*380a37e4SHao Wu sel ? s->max_rpm[2] : s->max_rpm[0], 232*380a37e4SHao Wu sel ? s->duty[2] : s->duty[0], 233*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CPA], 234*380a37e4SHao Wu cpcfg, 235*380a37e4SHao Wu &s->regs[R_NPCM7XX_MFT_CNT1]); 236*380a37e4SHao Wu switch (state) { 237*380a37e4SHao Wu case NPCM7XX_CAPTURE_SUCCEED: 238*380a37e4SHao Wu /* Interrupt on input capture on TAn transition - TAPND */ 239*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CRA] = s->regs[R_NPCM7XX_MFT_CNT1]; 240*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TAPND; 241*380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TAIEN) { 242*380a37e4SHao Wu irq_level = 1; 243*380a37e4SHao Wu } 244*380a37e4SHao Wu break; 245*380a37e4SHao Wu 246*380a37e4SHao Wu case NPCM7XX_CAPTURE_COMPARE_HIT: 247*380a37e4SHao Wu /* Compare Hit - TEPND */ 248*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TEPND; 249*380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TEIEN) { 250*380a37e4SHao Wu irq_level = 1; 251*380a37e4SHao Wu } 252*380a37e4SHao Wu break; 253*380a37e4SHao Wu 254*380a37e4SHao Wu case NPCM7XX_CAPTURE_UNDERFLOW: 255*380a37e4SHao Wu /* Underflow - TCPND */ 256*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TCPND; 257*380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TCIEN) { 258*380a37e4SHao Wu irq_level = 1; 259*380a37e4SHao Wu } 260*380a37e4SHao Wu break; 261*380a37e4SHao Wu 262*380a37e4SHao Wu default: 263*380a37e4SHao Wu g_assert_not_reached(); 264*380a37e4SHao Wu } 265*380a37e4SHao Wu } 266*380a37e4SHao Wu 267*380a37e4SHao Wu /* Capture input B. */ 268*380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TBEN && 269*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) { 270*380a37e4SHao Wu sel = s->regs[R_NPCM7XX_MFT_INBSEL] & NPCM7XX_MFT_INBSEL_SELB; 271*380a37e4SHao Wu cpcfg = NPCM7XX_MFT_CPCFG_GET_B(s->regs[R_NPCM7XX_MFT_CPCFG]); 272*380a37e4SHao Wu state = npcm7xx_mft_compute_cnt(s->clock_2, 273*380a37e4SHao Wu sel ? s->max_rpm[3] : s->max_rpm[1], 274*380a37e4SHao Wu sel ? s->duty[3] : s->duty[1], 275*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CPB], 276*380a37e4SHao Wu cpcfg, 277*380a37e4SHao Wu &s->regs[R_NPCM7XX_MFT_CNT2]); 278*380a37e4SHao Wu switch (state) { 279*380a37e4SHao Wu case NPCM7XX_CAPTURE_SUCCEED: 280*380a37e4SHao Wu /* Interrupt on input capture on TBn transition - TBPND */ 281*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CRB] = s->regs[R_NPCM7XX_MFT_CNT2]; 282*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TBPND; 283*380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TBIEN) { 284*380a37e4SHao Wu irq_level = 1; 285*380a37e4SHao Wu } 286*380a37e4SHao Wu break; 287*380a37e4SHao Wu 288*380a37e4SHao Wu case NPCM7XX_CAPTURE_COMPARE_HIT: 289*380a37e4SHao Wu /* Compare Hit - TFPND */ 290*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TFPND; 291*380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TFIEN) { 292*380a37e4SHao Wu irq_level = 1; 293*380a37e4SHao Wu } 294*380a37e4SHao Wu break; 295*380a37e4SHao Wu 296*380a37e4SHao Wu case NPCM7XX_CAPTURE_UNDERFLOW: 297*380a37e4SHao Wu /* Underflow - TDPND */ 298*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TDPND; 299*380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TDIEN) { 300*380a37e4SHao Wu irq_level = 1; 301*380a37e4SHao Wu } 302*380a37e4SHao Wu break; 303*380a37e4SHao Wu 304*380a37e4SHao Wu default: 305*380a37e4SHao Wu g_assert_not_reached(); 306*380a37e4SHao Wu } 307*380a37e4SHao Wu } 308*380a37e4SHao Wu 309*380a37e4SHao Wu trace_npcm7xx_mft_capture(DEVICE(s)->canonical_path, irq_level); 310*380a37e4SHao Wu qemu_set_irq(s->irq, irq_level); 311*380a37e4SHao Wu } 312*380a37e4SHao Wu 313*380a37e4SHao Wu /* Update clock for counters. */ 314*380a37e4SHao Wu static void npcm7xx_mft_update_clock(void *opaque, ClockEvent event) 315*380a37e4SHao Wu { 316*380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 317*380a37e4SHao Wu uint64_t prescaled_clock_period; 318*380a37e4SHao Wu 319*380a37e4SHao Wu prescaled_clock_period = clock_get(s->clock_in) * 320*380a37e4SHao Wu (s->regs[R_NPCM7XX_MFT_PRSC] + 1ULL); 321*380a37e4SHao Wu trace_npcm7xx_mft_update_clock(s->clock_in->canonical_path, 322*380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CKC], 323*380a37e4SHao Wu clock_get(s->clock_in), 324*380a37e4SHao Wu prescaled_clock_period); 325*380a37e4SHao Wu /* Update clock 1 */ 326*380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) { 327*380a37e4SHao Wu /* Clock is prescaled. */ 328*380a37e4SHao Wu clock_update(s->clock_1, prescaled_clock_period); 329*380a37e4SHao Wu } else { 330*380a37e4SHao Wu /* Clock stopped. */ 331*380a37e4SHao Wu clock_update(s->clock_1, 0); 332*380a37e4SHao Wu } 333*380a37e4SHao Wu /* Update clock 2 */ 334*380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) { 335*380a37e4SHao Wu /* Clock is prescaled. */ 336*380a37e4SHao Wu clock_update(s->clock_2, prescaled_clock_period); 337*380a37e4SHao Wu } else { 338*380a37e4SHao Wu /* Clock stopped. */ 339*380a37e4SHao Wu clock_update(s->clock_2, 0); 340*380a37e4SHao Wu } 341*380a37e4SHao Wu 342*380a37e4SHao Wu npcm7xx_mft_capture(s); 343*380a37e4SHao Wu } 344*380a37e4SHao Wu 345*380a37e4SHao Wu static uint64_t npcm7xx_mft_read(void *opaque, hwaddr offset, unsigned size) 346*380a37e4SHao Wu { 347*380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 348*380a37e4SHao Wu uint16_t value = 0; 349*380a37e4SHao Wu 350*380a37e4SHao Wu switch (offset) { 351*380a37e4SHao Wu case A_NPCM7XX_MFT_ICLR: 352*380a37e4SHao Wu qemu_log_mask(LOG_GUEST_ERROR, 353*380a37e4SHao Wu "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n", 354*380a37e4SHao Wu __func__, offset); 355*380a37e4SHao Wu break; 356*380a37e4SHao Wu 357*380a37e4SHao Wu default: 358*380a37e4SHao Wu value = s->regs[offset / 2]; 359*380a37e4SHao Wu } 360*380a37e4SHao Wu 361*380a37e4SHao Wu trace_npcm7xx_mft_read(DEVICE(s)->canonical_path, offset, value); 362*380a37e4SHao Wu return value; 363*380a37e4SHao Wu } 364*380a37e4SHao Wu 365*380a37e4SHao Wu static void npcm7xx_mft_write(void *opaque, hwaddr offset, 366*380a37e4SHao Wu uint64_t v, unsigned size) 367*380a37e4SHao Wu { 368*380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 369*380a37e4SHao Wu 370*380a37e4SHao Wu trace_npcm7xx_mft_write(DEVICE(s)->canonical_path, offset, v); 371*380a37e4SHao Wu switch (offset) { 372*380a37e4SHao Wu case A_NPCM7XX_MFT_ICLR: 373*380a37e4SHao Wu npcm7xx_mft_clear_interrupt(s, v); 374*380a37e4SHao Wu break; 375*380a37e4SHao Wu 376*380a37e4SHao Wu case A_NPCM7XX_MFT_CKC: 377*380a37e4SHao Wu case A_NPCM7XX_MFT_PRSC: 378*380a37e4SHao Wu s->regs[offset / 2] = v; 379*380a37e4SHao Wu npcm7xx_mft_update_clock(s, ClockUpdate); 380*380a37e4SHao Wu break; 381*380a37e4SHao Wu 382*380a37e4SHao Wu default: 383*380a37e4SHao Wu s->regs[offset / 2] = v; 384*380a37e4SHao Wu npcm7xx_mft_capture(s); 385*380a37e4SHao Wu break; 386*380a37e4SHao Wu } 387*380a37e4SHao Wu } 388*380a37e4SHao Wu 389*380a37e4SHao Wu static bool npcm7xx_mft_check_mem_op(void *opaque, hwaddr offset, 390*380a37e4SHao Wu unsigned size, bool is_write, 391*380a37e4SHao Wu MemTxAttrs attrs) 392*380a37e4SHao Wu { 393*380a37e4SHao Wu switch (offset) { 394*380a37e4SHao Wu /* 16-bit registers. Must be accessed with 16-bit read/write.*/ 395*380a37e4SHao Wu case A_NPCM7XX_MFT_CNT1: 396*380a37e4SHao Wu case A_NPCM7XX_MFT_CRA: 397*380a37e4SHao Wu case A_NPCM7XX_MFT_CRB: 398*380a37e4SHao Wu case A_NPCM7XX_MFT_CNT2: 399*380a37e4SHao Wu case A_NPCM7XX_MFT_CPA: 400*380a37e4SHao Wu case A_NPCM7XX_MFT_CPB: 401*380a37e4SHao Wu return size == 2; 402*380a37e4SHao Wu 403*380a37e4SHao Wu /* 8-bit registers. Must be accessed with 8-bit read/write.*/ 404*380a37e4SHao Wu case A_NPCM7XX_MFT_PRSC: 405*380a37e4SHao Wu case A_NPCM7XX_MFT_CKC: 406*380a37e4SHao Wu case A_NPCM7XX_MFT_MCTRL: 407*380a37e4SHao Wu case A_NPCM7XX_MFT_ICTRL: 408*380a37e4SHao Wu case A_NPCM7XX_MFT_ICLR: 409*380a37e4SHao Wu case A_NPCM7XX_MFT_IEN: 410*380a37e4SHao Wu case A_NPCM7XX_MFT_CPCFG: 411*380a37e4SHao Wu case A_NPCM7XX_MFT_INASEL: 412*380a37e4SHao Wu case A_NPCM7XX_MFT_INBSEL: 413*380a37e4SHao Wu return size == 1; 414*380a37e4SHao Wu 415*380a37e4SHao Wu default: 416*380a37e4SHao Wu /* Invalid registers. */ 417*380a37e4SHao Wu return false; 418*380a37e4SHao Wu } 419*380a37e4SHao Wu } 420*380a37e4SHao Wu 421*380a37e4SHao Wu static void npcm7xx_mft_get_max_rpm(Object *obj, Visitor *v, const char *name, 422*380a37e4SHao Wu void *opaque, Error **errp) 423*380a37e4SHao Wu { 424*380a37e4SHao Wu visit_type_uint32(v, name, (uint32_t *)opaque, errp); 425*380a37e4SHao Wu } 426*380a37e4SHao Wu 427*380a37e4SHao Wu static void npcm7xx_mft_set_max_rpm(Object *obj, Visitor *v, const char *name, 428*380a37e4SHao Wu void *opaque, Error **errp) 429*380a37e4SHao Wu { 430*380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 431*380a37e4SHao Wu uint32_t *max_rpm = opaque; 432*380a37e4SHao Wu uint32_t value; 433*380a37e4SHao Wu 434*380a37e4SHao Wu if (!visit_type_uint32(v, name, &value, errp)) { 435*380a37e4SHao Wu return; 436*380a37e4SHao Wu } 437*380a37e4SHao Wu 438*380a37e4SHao Wu *max_rpm = value; 439*380a37e4SHao Wu npcm7xx_mft_capture(s); 440*380a37e4SHao Wu } 441*380a37e4SHao Wu 442*380a37e4SHao Wu static void npcm7xx_mft_duty_handler(void *opaque, int n, int value) 443*380a37e4SHao Wu { 444*380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 445*380a37e4SHao Wu 446*380a37e4SHao Wu trace_npcm7xx_mft_set_duty(DEVICE(s)->canonical_path, n, value); 447*380a37e4SHao Wu s->duty[n] = value; 448*380a37e4SHao Wu npcm7xx_mft_capture(s); 449*380a37e4SHao Wu } 450*380a37e4SHao Wu 451*380a37e4SHao Wu static const struct MemoryRegionOps npcm7xx_mft_ops = { 452*380a37e4SHao Wu .read = npcm7xx_mft_read, 453*380a37e4SHao Wu .write = npcm7xx_mft_write, 454*380a37e4SHao Wu .endianness = DEVICE_LITTLE_ENDIAN, 455*380a37e4SHao Wu .valid = { 456*380a37e4SHao Wu .min_access_size = 1, 457*380a37e4SHao Wu .max_access_size = 2, 458*380a37e4SHao Wu .unaligned = false, 459*380a37e4SHao Wu .accepts = npcm7xx_mft_check_mem_op, 460*380a37e4SHao Wu }, 461*380a37e4SHao Wu }; 462*380a37e4SHao Wu 463*380a37e4SHao Wu static void npcm7xx_mft_enter_reset(Object *obj, ResetType type) 464*380a37e4SHao Wu { 465*380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 466*380a37e4SHao Wu 467*380a37e4SHao Wu npcm7xx_mft_reset(s); 468*380a37e4SHao Wu } 469*380a37e4SHao Wu 470*380a37e4SHao Wu static void npcm7xx_mft_hold_reset(Object *obj) 471*380a37e4SHao Wu { 472*380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 473*380a37e4SHao Wu 474*380a37e4SHao Wu qemu_irq_lower(s->irq); 475*380a37e4SHao Wu } 476*380a37e4SHao Wu 477*380a37e4SHao Wu static void npcm7xx_mft_init(Object *obj) 478*380a37e4SHao Wu { 479*380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 480*380a37e4SHao Wu SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 481*380a37e4SHao Wu DeviceState *dev = DEVICE(obj); 482*380a37e4SHao Wu 483*380a37e4SHao Wu memory_region_init_io(&s->iomem, obj, &npcm7xx_mft_ops, s, 484*380a37e4SHao Wu TYPE_NPCM7XX_MFT, 4 * KiB); 485*380a37e4SHao Wu sysbus_init_mmio(sbd, &s->iomem); 486*380a37e4SHao Wu sysbus_init_irq(sbd, &s->irq); 487*380a37e4SHao Wu s->clock_in = qdev_init_clock_in(dev, "clock-in", npcm7xx_mft_update_clock, 488*380a37e4SHao Wu s, ClockUpdate); 489*380a37e4SHao Wu s->clock_1 = qdev_init_clock_out(dev, "clock1"); 490*380a37e4SHao Wu s->clock_2 = qdev_init_clock_out(dev, "clock2"); 491*380a37e4SHao Wu 492*380a37e4SHao Wu for (int i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) { 493*380a37e4SHao Wu object_property_add(obj, "max_rpm[*]", "uint32", 494*380a37e4SHao Wu npcm7xx_mft_get_max_rpm, 495*380a37e4SHao Wu npcm7xx_mft_set_max_rpm, 496*380a37e4SHao Wu NULL, &s->max_rpm[i]); 497*380a37e4SHao Wu } 498*380a37e4SHao Wu qdev_init_gpio_in_named(dev, npcm7xx_mft_duty_handler, "duty", 499*380a37e4SHao Wu NPCM7XX_MFT_FANIN_COUNT); 500*380a37e4SHao Wu } 501*380a37e4SHao Wu 502*380a37e4SHao Wu static const VMStateDescription vmstate_npcm7xx_mft = { 503*380a37e4SHao Wu .name = "npcm7xx-mft-module", 504*380a37e4SHao Wu .version_id = 0, 505*380a37e4SHao Wu .minimum_version_id = 0, 506*380a37e4SHao Wu .fields = (VMStateField[]) { 507*380a37e4SHao Wu VMSTATE_CLOCK(clock_in, NPCM7xxMFTState), 508*380a37e4SHao Wu VMSTATE_CLOCK(clock_1, NPCM7xxMFTState), 509*380a37e4SHao Wu VMSTATE_CLOCK(clock_2, NPCM7xxMFTState), 510*380a37e4SHao Wu VMSTATE_UINT16_ARRAY(regs, NPCM7xxMFTState, NPCM7XX_MFT_NR_REGS), 511*380a37e4SHao Wu VMSTATE_UINT32_ARRAY(max_rpm, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT), 512*380a37e4SHao Wu VMSTATE_UINT32_ARRAY(duty, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT), 513*380a37e4SHao Wu VMSTATE_END_OF_LIST(), 514*380a37e4SHao Wu }, 515*380a37e4SHao Wu }; 516*380a37e4SHao Wu 517*380a37e4SHao Wu static void npcm7xx_mft_class_init(ObjectClass *klass, void *data) 518*380a37e4SHao Wu { 519*380a37e4SHao Wu ResettableClass *rc = RESETTABLE_CLASS(klass); 520*380a37e4SHao Wu DeviceClass *dc = DEVICE_CLASS(klass); 521*380a37e4SHao Wu 522*380a37e4SHao Wu dc->desc = "NPCM7xx MFT Controller"; 523*380a37e4SHao Wu dc->vmsd = &vmstate_npcm7xx_mft; 524*380a37e4SHao Wu rc->phases.enter = npcm7xx_mft_enter_reset; 525*380a37e4SHao Wu rc->phases.hold = npcm7xx_mft_hold_reset; 526*380a37e4SHao Wu } 527*380a37e4SHao Wu 528*380a37e4SHao Wu static const TypeInfo npcm7xx_mft_info = { 529*380a37e4SHao Wu .name = TYPE_NPCM7XX_MFT, 530*380a37e4SHao Wu .parent = TYPE_SYS_BUS_DEVICE, 531*380a37e4SHao Wu .instance_size = sizeof(NPCM7xxMFTState), 532*380a37e4SHao Wu .class_init = npcm7xx_mft_class_init, 533*380a37e4SHao Wu .instance_init = npcm7xx_mft_init, 534*380a37e4SHao Wu }; 535*380a37e4SHao Wu 536*380a37e4SHao Wu static void npcm7xx_mft_register_type(void) 537*380a37e4SHao Wu { 538*380a37e4SHao Wu type_register_static(&npcm7xx_mft_info); 539*380a37e4SHao Wu } 540*380a37e4SHao Wu type_init(npcm7xx_mft_register_type); 541