1380a37e4SHao Wu /* 2380a37e4SHao Wu * Nuvoton NPCM7xx MFT Module 3380a37e4SHao Wu * 4380a37e4SHao Wu * Copyright 2021 Google LLC 5380a37e4SHao Wu * 6380a37e4SHao Wu * This program is free software; you can redistribute it and/or modify it 7380a37e4SHao Wu * under the terms of the GNU General Public License as published by the 8380a37e4SHao Wu * Free Software Foundation; either version 2 of the License, or 9380a37e4SHao Wu * (at your option) any later version. 10380a37e4SHao Wu * 11380a37e4SHao Wu * This program is distributed in the hope that it will be useful, but WITHOUT 12380a37e4SHao Wu * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13380a37e4SHao Wu * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14380a37e4SHao Wu * for more details. 15380a37e4SHao Wu */ 16380a37e4SHao Wu 17380a37e4SHao Wu #include "qemu/osdep.h" 18380a37e4SHao Wu #include "hw/irq.h" 19380a37e4SHao Wu #include "hw/qdev-clock.h" 20380a37e4SHao Wu #include "hw/qdev-properties.h" 21380a37e4SHao Wu #include "hw/misc/npcm7xx_mft.h" 22380a37e4SHao Wu #include "hw/misc/npcm7xx_pwm.h" 23380a37e4SHao Wu #include "hw/registerfields.h" 24380a37e4SHao Wu #include "migration/vmstate.h" 25380a37e4SHao Wu #include "qapi/error.h" 26380a37e4SHao Wu #include "qapi/visitor.h" 27380a37e4SHao Wu #include "qemu/bitops.h" 28380a37e4SHao Wu #include "qemu/error-report.h" 29380a37e4SHao Wu #include "qemu/log.h" 30380a37e4SHao Wu #include "qemu/module.h" 31380a37e4SHao Wu #include "qemu/timer.h" 32380a37e4SHao Wu #include "qemu/units.h" 33380a37e4SHao Wu #include "trace.h" 34380a37e4SHao Wu 35380a37e4SHao Wu /* 36380a37e4SHao Wu * Some of the registers can only accessed via 16-bit ops and some can only 37380a37e4SHao Wu * be accessed via 8-bit ops. However we mark all of them using REG16 to 38380a37e4SHao Wu * simplify implementation. npcm7xx_mft_check_mem_op checks the access length 39380a37e4SHao Wu * of memory operations. 40380a37e4SHao Wu */ 41380a37e4SHao Wu REG16(NPCM7XX_MFT_CNT1, 0x00); 42380a37e4SHao Wu REG16(NPCM7XX_MFT_CRA, 0x02); 43380a37e4SHao Wu REG16(NPCM7XX_MFT_CRB, 0x04); 44380a37e4SHao Wu REG16(NPCM7XX_MFT_CNT2, 0x06); 45380a37e4SHao Wu REG16(NPCM7XX_MFT_PRSC, 0x08); 46380a37e4SHao Wu REG16(NPCM7XX_MFT_CKC, 0x0a); 47380a37e4SHao Wu REG16(NPCM7XX_MFT_MCTRL, 0x0c); 48380a37e4SHao Wu REG16(NPCM7XX_MFT_ICTRL, 0x0e); 49380a37e4SHao Wu REG16(NPCM7XX_MFT_ICLR, 0x10); 50380a37e4SHao Wu REG16(NPCM7XX_MFT_IEN, 0x12); 51380a37e4SHao Wu REG16(NPCM7XX_MFT_CPA, 0x14); 52380a37e4SHao Wu REG16(NPCM7XX_MFT_CPB, 0x16); 53380a37e4SHao Wu REG16(NPCM7XX_MFT_CPCFG, 0x18); 54380a37e4SHao Wu REG16(NPCM7XX_MFT_INASEL, 0x1a); 55380a37e4SHao Wu REG16(NPCM7XX_MFT_INBSEL, 0x1c); 56380a37e4SHao Wu 57380a37e4SHao Wu /* Register Fields */ 58380a37e4SHao Wu #define NPCM7XX_MFT_CKC_C2CSEL BIT(3) 59380a37e4SHao Wu #define NPCM7XX_MFT_CKC_C1CSEL BIT(0) 60380a37e4SHao Wu 61380a37e4SHao Wu #define NPCM7XX_MFT_MCTRL_TBEN BIT(6) 62380a37e4SHao Wu #define NPCM7XX_MFT_MCTRL_TAEN BIT(5) 63380a37e4SHao Wu #define NPCM7XX_MFT_MCTRL_TBEDG BIT(4) 64380a37e4SHao Wu #define NPCM7XX_MFT_MCTRL_TAEDG BIT(3) 65380a37e4SHao Wu #define NPCM7XX_MFT_MCTRL_MODE5 BIT(2) 66380a37e4SHao Wu 67380a37e4SHao Wu #define NPCM7XX_MFT_ICTRL_TFPND BIT(5) 68380a37e4SHao Wu #define NPCM7XX_MFT_ICTRL_TEPND BIT(4) 69380a37e4SHao Wu #define NPCM7XX_MFT_ICTRL_TDPND BIT(3) 70380a37e4SHao Wu #define NPCM7XX_MFT_ICTRL_TCPND BIT(2) 71380a37e4SHao Wu #define NPCM7XX_MFT_ICTRL_TBPND BIT(1) 72380a37e4SHao Wu #define NPCM7XX_MFT_ICTRL_TAPND BIT(0) 73380a37e4SHao Wu 74380a37e4SHao Wu #define NPCM7XX_MFT_ICLR_TFCLR BIT(5) 75380a37e4SHao Wu #define NPCM7XX_MFT_ICLR_TECLR BIT(4) 76380a37e4SHao Wu #define NPCM7XX_MFT_ICLR_TDCLR BIT(3) 77380a37e4SHao Wu #define NPCM7XX_MFT_ICLR_TCCLR BIT(2) 78380a37e4SHao Wu #define NPCM7XX_MFT_ICLR_TBCLR BIT(1) 79380a37e4SHao Wu #define NPCM7XX_MFT_ICLR_TACLR BIT(0) 80380a37e4SHao Wu 81380a37e4SHao Wu #define NPCM7XX_MFT_IEN_TFIEN BIT(5) 82380a37e4SHao Wu #define NPCM7XX_MFT_IEN_TEIEN BIT(4) 83380a37e4SHao Wu #define NPCM7XX_MFT_IEN_TDIEN BIT(3) 84380a37e4SHao Wu #define NPCM7XX_MFT_IEN_TCIEN BIT(2) 85380a37e4SHao Wu #define NPCM7XX_MFT_IEN_TBIEN BIT(1) 86380a37e4SHao Wu #define NPCM7XX_MFT_IEN_TAIEN BIT(0) 87380a37e4SHao Wu 88380a37e4SHao Wu #define NPCM7XX_MFT_CPCFG_GET_B(rv) extract8((rv), 4, 4) 89380a37e4SHao Wu #define NPCM7XX_MFT_CPCFG_GET_A(rv) extract8((rv), 0, 4) 90380a37e4SHao Wu #define NPCM7XX_MFT_CPCFG_HIEN BIT(3) 91380a37e4SHao Wu #define NPCM7XX_MFT_CPCFG_EQEN BIT(2) 92380a37e4SHao Wu #define NPCM7XX_MFT_CPCFG_LOEN BIT(1) 93380a37e4SHao Wu #define NPCM7XX_MFT_CPCFG_CPSEL BIT(0) 94380a37e4SHao Wu 95380a37e4SHao Wu #define NPCM7XX_MFT_INASEL_SELA BIT(0) 96380a37e4SHao Wu #define NPCM7XX_MFT_INBSEL_SELB BIT(0) 97380a37e4SHao Wu 98380a37e4SHao Wu /* Max CNT values of the module. The CNT value is a countdown from it. */ 99380a37e4SHao Wu #define NPCM7XX_MFT_MAX_CNT 0xFFFF 100380a37e4SHao Wu 101380a37e4SHao Wu /* Each fan revolution should generated 2 pulses */ 102380a37e4SHao Wu #define NPCM7XX_MFT_PULSE_PER_REVOLUTION 2 103380a37e4SHao Wu 104380a37e4SHao Wu typedef enum NPCM7xxMFTCaptureState { 105380a37e4SHao Wu /* capture succeeded with a valid CNT value. */ 106380a37e4SHao Wu NPCM7XX_CAPTURE_SUCCEED, 107380a37e4SHao Wu /* capture stopped prematurely due to reaching CPCFG condition. */ 108380a37e4SHao Wu NPCM7XX_CAPTURE_COMPARE_HIT, 109380a37e4SHao Wu /* capture fails since it reaches underflow condition for CNT. */ 110380a37e4SHao Wu NPCM7XX_CAPTURE_UNDERFLOW, 111380a37e4SHao Wu } NPCM7xxMFTCaptureState; 112380a37e4SHao Wu 113380a37e4SHao Wu static void npcm7xx_mft_reset(NPCM7xxMFTState *s) 114380a37e4SHao Wu { 115380a37e4SHao Wu int i; 116380a37e4SHao Wu 117380a37e4SHao Wu /* Only registers PRSC ~ INBSEL need to be reset. */ 118380a37e4SHao Wu for (i = R_NPCM7XX_MFT_PRSC; i <= R_NPCM7XX_MFT_INBSEL; ++i) { 119380a37e4SHao Wu s->regs[i] = 0; 120380a37e4SHao Wu } 121380a37e4SHao Wu } 122380a37e4SHao Wu 123380a37e4SHao Wu static void npcm7xx_mft_clear_interrupt(NPCM7xxMFTState *s, uint8_t iclr) 124380a37e4SHao Wu { 125380a37e4SHao Wu /* 126380a37e4SHao Wu * Clear bits in ICTRL where corresponding bits in iclr is 1. 127380a37e4SHao Wu * Both iclr and ictrl are 8-bit regs. (See npcm7xx_mft_check_mem_op) 128380a37e4SHao Wu */ 129380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] &= ~iclr; 130380a37e4SHao Wu } 131380a37e4SHao Wu 132380a37e4SHao Wu /* 133380a37e4SHao Wu * If the CPCFG's condition should be triggered during count down from 134380a37e4SHao Wu * NPCM7XX_MFT_MAX_CNT to src if compared to tgt, return the count when 135380a37e4SHao Wu * the condition is triggered. 136380a37e4SHao Wu * Otherwise return -1. 137380a37e4SHao Wu * Since tgt is uint16_t it must always <= NPCM7XX_MFT_MAX_CNT. 138380a37e4SHao Wu */ 139380a37e4SHao Wu static int npcm7xx_mft_compare(int32_t src, uint16_t tgt, uint8_t cpcfg) 140380a37e4SHao Wu { 141380a37e4SHao Wu if (cpcfg & NPCM7XX_MFT_CPCFG_HIEN) { 142380a37e4SHao Wu return NPCM7XX_MFT_MAX_CNT; 143380a37e4SHao Wu } 144380a37e4SHao Wu if ((cpcfg & NPCM7XX_MFT_CPCFG_EQEN) && (src <= tgt)) { 145380a37e4SHao Wu return tgt; 146380a37e4SHao Wu } 147380a37e4SHao Wu if ((cpcfg & NPCM7XX_MFT_CPCFG_LOEN) && (tgt > 0) && (src < tgt)) { 148380a37e4SHao Wu return tgt - 1; 149380a37e4SHao Wu } 150380a37e4SHao Wu 151380a37e4SHao Wu return -1; 152380a37e4SHao Wu } 153380a37e4SHao Wu 154380a37e4SHao Wu /* Compute CNT according to corresponding fan's RPM. */ 155380a37e4SHao Wu static NPCM7xxMFTCaptureState npcm7xx_mft_compute_cnt( 156380a37e4SHao Wu Clock *clock, uint32_t max_rpm, uint32_t duty, uint16_t tgt, 157380a37e4SHao Wu uint8_t cpcfg, uint16_t *cnt) 158380a37e4SHao Wu { 159380a37e4SHao Wu uint32_t rpm = (uint64_t)max_rpm * (uint64_t)duty / NPCM7XX_PWM_MAX_DUTY; 160380a37e4SHao Wu int32_t count; 161380a37e4SHao Wu int stopped; 162380a37e4SHao Wu NPCM7xxMFTCaptureState state; 163380a37e4SHao Wu 164380a37e4SHao Wu if (rpm == 0) { 165380a37e4SHao Wu /* 166380a37e4SHao Wu * If RPM = 0, capture won't happen. CNT will continue count down. 167380a37e4SHao Wu * So it's effective equivalent to have a cnt > NPCM7XX_MFT_MAX_CNT 168380a37e4SHao Wu */ 169380a37e4SHao Wu count = NPCM7XX_MFT_MAX_CNT + 1; 170380a37e4SHao Wu } else { 171380a37e4SHao Wu /* 172380a37e4SHao Wu * RPM = revolution/min. The time for one revlution (in ns) is 173380a37e4SHao Wu * MINUTE_TO_NANOSECOND / RPM. 174380a37e4SHao Wu */ 175*593b910eSTigran Sogomonian count = clock_ns_to_ticks(clock, 176*593b910eSTigran Sogomonian (uint64_t)(60 * NANOSECONDS_PER_SECOND) / 177*593b910eSTigran Sogomonian ((uint64_t)rpm * NPCM7XX_MFT_PULSE_PER_REVOLUTION)); 178380a37e4SHao Wu } 179380a37e4SHao Wu 180380a37e4SHao Wu if (count > NPCM7XX_MFT_MAX_CNT) { 181380a37e4SHao Wu count = -1; 182380a37e4SHao Wu } else { 183380a37e4SHao Wu /* The CNT is a countdown value from NPCM7XX_MFT_MAX_CNT. */ 184380a37e4SHao Wu count = NPCM7XX_MFT_MAX_CNT - count; 185380a37e4SHao Wu } 186380a37e4SHao Wu stopped = npcm7xx_mft_compare(count, tgt, cpcfg); 187380a37e4SHao Wu if (stopped == -1) { 188380a37e4SHao Wu if (count == -1) { 189380a37e4SHao Wu /* Underflow */ 190380a37e4SHao Wu state = NPCM7XX_CAPTURE_UNDERFLOW; 191380a37e4SHao Wu } else { 192380a37e4SHao Wu state = NPCM7XX_CAPTURE_SUCCEED; 193380a37e4SHao Wu } 194380a37e4SHao Wu } else { 195380a37e4SHao Wu count = stopped; 196380a37e4SHao Wu state = NPCM7XX_CAPTURE_COMPARE_HIT; 197380a37e4SHao Wu } 198380a37e4SHao Wu 199380a37e4SHao Wu if (count != -1) { 200380a37e4SHao Wu *cnt = count; 201380a37e4SHao Wu } 202380a37e4SHao Wu trace_npcm7xx_mft_rpm(clock->canonical_path, clock_get_hz(clock), 203380a37e4SHao Wu state, count, rpm, duty); 204380a37e4SHao Wu return state; 205380a37e4SHao Wu } 206380a37e4SHao Wu 207380a37e4SHao Wu /* 208380a37e4SHao Wu * Capture Fan RPM and update CNT and CR registers accordingly. 209380a37e4SHao Wu * Raise IRQ if certain contidions are met in IEN. 210380a37e4SHao Wu */ 211380a37e4SHao Wu static void npcm7xx_mft_capture(NPCM7xxMFTState *s) 212380a37e4SHao Wu { 213380a37e4SHao Wu int irq_level = 0; 214380a37e4SHao Wu NPCM7xxMFTCaptureState state; 215380a37e4SHao Wu int sel; 216380a37e4SHao Wu uint8_t cpcfg; 217380a37e4SHao Wu 218380a37e4SHao Wu /* 219380a37e4SHao Wu * If not mode 5, the behavior is undefined. We just do nothing in this 220380a37e4SHao Wu * case. 221380a37e4SHao Wu */ 222380a37e4SHao Wu if (!(s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_MODE5)) { 223380a37e4SHao Wu return; 224380a37e4SHao Wu } 225380a37e4SHao Wu 226380a37e4SHao Wu /* Capture input A. */ 227380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TAEN && 228380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) { 229380a37e4SHao Wu sel = s->regs[R_NPCM7XX_MFT_INASEL] & NPCM7XX_MFT_INASEL_SELA; 230380a37e4SHao Wu cpcfg = NPCM7XX_MFT_CPCFG_GET_A(s->regs[R_NPCM7XX_MFT_CPCFG]); 231380a37e4SHao Wu state = npcm7xx_mft_compute_cnt(s->clock_1, 232380a37e4SHao Wu sel ? s->max_rpm[2] : s->max_rpm[0], 233380a37e4SHao Wu sel ? s->duty[2] : s->duty[0], 234380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CPA], 235380a37e4SHao Wu cpcfg, 236380a37e4SHao Wu &s->regs[R_NPCM7XX_MFT_CNT1]); 237380a37e4SHao Wu switch (state) { 238380a37e4SHao Wu case NPCM7XX_CAPTURE_SUCCEED: 239380a37e4SHao Wu /* Interrupt on input capture on TAn transition - TAPND */ 240380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CRA] = s->regs[R_NPCM7XX_MFT_CNT1]; 241380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TAPND; 242380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TAIEN) { 243380a37e4SHao Wu irq_level = 1; 244380a37e4SHao Wu } 245380a37e4SHao Wu break; 246380a37e4SHao Wu 247380a37e4SHao Wu case NPCM7XX_CAPTURE_COMPARE_HIT: 248380a37e4SHao Wu /* Compare Hit - TEPND */ 249380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TEPND; 250380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TEIEN) { 251380a37e4SHao Wu irq_level = 1; 252380a37e4SHao Wu } 253380a37e4SHao Wu break; 254380a37e4SHao Wu 255380a37e4SHao Wu case NPCM7XX_CAPTURE_UNDERFLOW: 256380a37e4SHao Wu /* Underflow - TCPND */ 257380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TCPND; 258380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TCIEN) { 259380a37e4SHao Wu irq_level = 1; 260380a37e4SHao Wu } 261380a37e4SHao Wu break; 262380a37e4SHao Wu 263380a37e4SHao Wu default: 264380a37e4SHao Wu g_assert_not_reached(); 265380a37e4SHao Wu } 266380a37e4SHao Wu } 267380a37e4SHao Wu 268380a37e4SHao Wu /* Capture input B. */ 269380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TBEN && 270380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) { 271380a37e4SHao Wu sel = s->regs[R_NPCM7XX_MFT_INBSEL] & NPCM7XX_MFT_INBSEL_SELB; 272380a37e4SHao Wu cpcfg = NPCM7XX_MFT_CPCFG_GET_B(s->regs[R_NPCM7XX_MFT_CPCFG]); 273380a37e4SHao Wu state = npcm7xx_mft_compute_cnt(s->clock_2, 274380a37e4SHao Wu sel ? s->max_rpm[3] : s->max_rpm[1], 275380a37e4SHao Wu sel ? s->duty[3] : s->duty[1], 276380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CPB], 277380a37e4SHao Wu cpcfg, 278380a37e4SHao Wu &s->regs[R_NPCM7XX_MFT_CNT2]); 279380a37e4SHao Wu switch (state) { 280380a37e4SHao Wu case NPCM7XX_CAPTURE_SUCCEED: 281380a37e4SHao Wu /* Interrupt on input capture on TBn transition - TBPND */ 282380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CRB] = s->regs[R_NPCM7XX_MFT_CNT2]; 283380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TBPND; 284380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TBIEN) { 285380a37e4SHao Wu irq_level = 1; 286380a37e4SHao Wu } 287380a37e4SHao Wu break; 288380a37e4SHao Wu 289380a37e4SHao Wu case NPCM7XX_CAPTURE_COMPARE_HIT: 290380a37e4SHao Wu /* Compare Hit - TFPND */ 291380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TFPND; 292380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TFIEN) { 293380a37e4SHao Wu irq_level = 1; 294380a37e4SHao Wu } 295380a37e4SHao Wu break; 296380a37e4SHao Wu 297380a37e4SHao Wu case NPCM7XX_CAPTURE_UNDERFLOW: 298380a37e4SHao Wu /* Underflow - TDPND */ 299380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TDPND; 300380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TDIEN) { 301380a37e4SHao Wu irq_level = 1; 302380a37e4SHao Wu } 303380a37e4SHao Wu break; 304380a37e4SHao Wu 305380a37e4SHao Wu default: 306380a37e4SHao Wu g_assert_not_reached(); 307380a37e4SHao Wu } 308380a37e4SHao Wu } 309380a37e4SHao Wu 310380a37e4SHao Wu trace_npcm7xx_mft_capture(DEVICE(s)->canonical_path, irq_level); 311380a37e4SHao Wu qemu_set_irq(s->irq, irq_level); 312380a37e4SHao Wu } 313380a37e4SHao Wu 314380a37e4SHao Wu /* Update clock for counters. */ 315380a37e4SHao Wu static void npcm7xx_mft_update_clock(void *opaque, ClockEvent event) 316380a37e4SHao Wu { 317380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 318380a37e4SHao Wu uint64_t prescaled_clock_period; 319380a37e4SHao Wu 320380a37e4SHao Wu prescaled_clock_period = clock_get(s->clock_in) * 321380a37e4SHao Wu (s->regs[R_NPCM7XX_MFT_PRSC] + 1ULL); 322380a37e4SHao Wu trace_npcm7xx_mft_update_clock(s->clock_in->canonical_path, 323380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CKC], 324380a37e4SHao Wu clock_get(s->clock_in), 325380a37e4SHao Wu prescaled_clock_period); 326380a37e4SHao Wu /* Update clock 1 */ 327380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) { 328380a37e4SHao Wu /* Clock is prescaled. */ 329380a37e4SHao Wu clock_update(s->clock_1, prescaled_clock_period); 330380a37e4SHao Wu } else { 331380a37e4SHao Wu /* Clock stopped. */ 332380a37e4SHao Wu clock_update(s->clock_1, 0); 333380a37e4SHao Wu } 334380a37e4SHao Wu /* Update clock 2 */ 335380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) { 336380a37e4SHao Wu /* Clock is prescaled. */ 337380a37e4SHao Wu clock_update(s->clock_2, prescaled_clock_period); 338380a37e4SHao Wu } else { 339380a37e4SHao Wu /* Clock stopped. */ 340380a37e4SHao Wu clock_update(s->clock_2, 0); 341380a37e4SHao Wu } 342380a37e4SHao Wu 343380a37e4SHao Wu npcm7xx_mft_capture(s); 344380a37e4SHao Wu } 345380a37e4SHao Wu 346380a37e4SHao Wu static uint64_t npcm7xx_mft_read(void *opaque, hwaddr offset, unsigned size) 347380a37e4SHao Wu { 348380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 349380a37e4SHao Wu uint16_t value = 0; 350380a37e4SHao Wu 351380a37e4SHao Wu switch (offset) { 352380a37e4SHao Wu case A_NPCM7XX_MFT_ICLR: 353380a37e4SHao Wu qemu_log_mask(LOG_GUEST_ERROR, 354380a37e4SHao Wu "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n", 355380a37e4SHao Wu __func__, offset); 356380a37e4SHao Wu break; 357380a37e4SHao Wu 358380a37e4SHao Wu default: 359380a37e4SHao Wu value = s->regs[offset / 2]; 360380a37e4SHao Wu } 361380a37e4SHao Wu 362380a37e4SHao Wu trace_npcm7xx_mft_read(DEVICE(s)->canonical_path, offset, value); 363380a37e4SHao Wu return value; 364380a37e4SHao Wu } 365380a37e4SHao Wu 366380a37e4SHao Wu static void npcm7xx_mft_write(void *opaque, hwaddr offset, 367380a37e4SHao Wu uint64_t v, unsigned size) 368380a37e4SHao Wu { 369380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 370380a37e4SHao Wu 371380a37e4SHao Wu trace_npcm7xx_mft_write(DEVICE(s)->canonical_path, offset, v); 372380a37e4SHao Wu switch (offset) { 373380a37e4SHao Wu case A_NPCM7XX_MFT_ICLR: 374380a37e4SHao Wu npcm7xx_mft_clear_interrupt(s, v); 375380a37e4SHao Wu break; 376380a37e4SHao Wu 377380a37e4SHao Wu case A_NPCM7XX_MFT_CKC: 378380a37e4SHao Wu case A_NPCM7XX_MFT_PRSC: 379380a37e4SHao Wu s->regs[offset / 2] = v; 380380a37e4SHao Wu npcm7xx_mft_update_clock(s, ClockUpdate); 381380a37e4SHao Wu break; 382380a37e4SHao Wu 383380a37e4SHao Wu default: 384380a37e4SHao Wu s->regs[offset / 2] = v; 385380a37e4SHao Wu npcm7xx_mft_capture(s); 386380a37e4SHao Wu break; 387380a37e4SHao Wu } 388380a37e4SHao Wu } 389380a37e4SHao Wu 390380a37e4SHao Wu static bool npcm7xx_mft_check_mem_op(void *opaque, hwaddr offset, 391380a37e4SHao Wu unsigned size, bool is_write, 392380a37e4SHao Wu MemTxAttrs attrs) 393380a37e4SHao Wu { 394380a37e4SHao Wu switch (offset) { 395380a37e4SHao Wu /* 16-bit registers. Must be accessed with 16-bit read/write.*/ 396380a37e4SHao Wu case A_NPCM7XX_MFT_CNT1: 397380a37e4SHao Wu case A_NPCM7XX_MFT_CRA: 398380a37e4SHao Wu case A_NPCM7XX_MFT_CRB: 399380a37e4SHao Wu case A_NPCM7XX_MFT_CNT2: 400380a37e4SHao Wu case A_NPCM7XX_MFT_CPA: 401380a37e4SHao Wu case A_NPCM7XX_MFT_CPB: 402380a37e4SHao Wu return size == 2; 403380a37e4SHao Wu 404380a37e4SHao Wu /* 8-bit registers. Must be accessed with 8-bit read/write.*/ 405380a37e4SHao Wu case A_NPCM7XX_MFT_PRSC: 406380a37e4SHao Wu case A_NPCM7XX_MFT_CKC: 407380a37e4SHao Wu case A_NPCM7XX_MFT_MCTRL: 408380a37e4SHao Wu case A_NPCM7XX_MFT_ICTRL: 409380a37e4SHao Wu case A_NPCM7XX_MFT_ICLR: 410380a37e4SHao Wu case A_NPCM7XX_MFT_IEN: 411380a37e4SHao Wu case A_NPCM7XX_MFT_CPCFG: 412380a37e4SHao Wu case A_NPCM7XX_MFT_INASEL: 413380a37e4SHao Wu case A_NPCM7XX_MFT_INBSEL: 414380a37e4SHao Wu return size == 1; 415380a37e4SHao Wu 416380a37e4SHao Wu default: 417380a37e4SHao Wu /* Invalid registers. */ 418380a37e4SHao Wu return false; 419380a37e4SHao Wu } 420380a37e4SHao Wu } 421380a37e4SHao Wu 422380a37e4SHao Wu static void npcm7xx_mft_get_max_rpm(Object *obj, Visitor *v, const char *name, 423380a37e4SHao Wu void *opaque, Error **errp) 424380a37e4SHao Wu { 425380a37e4SHao Wu visit_type_uint32(v, name, (uint32_t *)opaque, errp); 426380a37e4SHao Wu } 427380a37e4SHao Wu 428380a37e4SHao Wu static void npcm7xx_mft_set_max_rpm(Object *obj, Visitor *v, const char *name, 429380a37e4SHao Wu void *opaque, Error **errp) 430380a37e4SHao Wu { 431380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 432380a37e4SHao Wu uint32_t *max_rpm = opaque; 433380a37e4SHao Wu uint32_t value; 434380a37e4SHao Wu 435380a37e4SHao Wu if (!visit_type_uint32(v, name, &value, errp)) { 436380a37e4SHao Wu return; 437380a37e4SHao Wu } 438380a37e4SHao Wu 439380a37e4SHao Wu *max_rpm = value; 440380a37e4SHao Wu npcm7xx_mft_capture(s); 441380a37e4SHao Wu } 442380a37e4SHao Wu 443380a37e4SHao Wu static void npcm7xx_mft_duty_handler(void *opaque, int n, int value) 444380a37e4SHao Wu { 445380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 446380a37e4SHao Wu 447380a37e4SHao Wu trace_npcm7xx_mft_set_duty(DEVICE(s)->canonical_path, n, value); 448380a37e4SHao Wu s->duty[n] = value; 449380a37e4SHao Wu npcm7xx_mft_capture(s); 450380a37e4SHao Wu } 451380a37e4SHao Wu 452380a37e4SHao Wu static const struct MemoryRegionOps npcm7xx_mft_ops = { 453380a37e4SHao Wu .read = npcm7xx_mft_read, 454380a37e4SHao Wu .write = npcm7xx_mft_write, 455380a37e4SHao Wu .endianness = DEVICE_LITTLE_ENDIAN, 456380a37e4SHao Wu .valid = { 457380a37e4SHao Wu .min_access_size = 1, 458380a37e4SHao Wu .max_access_size = 2, 459380a37e4SHao Wu .unaligned = false, 460380a37e4SHao Wu .accepts = npcm7xx_mft_check_mem_op, 461380a37e4SHao Wu }, 462380a37e4SHao Wu }; 463380a37e4SHao Wu 464380a37e4SHao Wu static void npcm7xx_mft_enter_reset(Object *obj, ResetType type) 465380a37e4SHao Wu { 466380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 467380a37e4SHao Wu 468380a37e4SHao Wu npcm7xx_mft_reset(s); 469380a37e4SHao Wu } 470380a37e4SHao Wu 471ad80e367SPeter Maydell static void npcm7xx_mft_hold_reset(Object *obj, ResetType type) 472380a37e4SHao Wu { 473380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 474380a37e4SHao Wu 475380a37e4SHao Wu qemu_irq_lower(s->irq); 476380a37e4SHao Wu } 477380a37e4SHao Wu 478380a37e4SHao Wu static void npcm7xx_mft_init(Object *obj) 479380a37e4SHao Wu { 480380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 481380a37e4SHao Wu SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 482380a37e4SHao Wu DeviceState *dev = DEVICE(obj); 483380a37e4SHao Wu 484380a37e4SHao Wu memory_region_init_io(&s->iomem, obj, &npcm7xx_mft_ops, s, 485380a37e4SHao Wu TYPE_NPCM7XX_MFT, 4 * KiB); 486380a37e4SHao Wu sysbus_init_mmio(sbd, &s->iomem); 487380a37e4SHao Wu sysbus_init_irq(sbd, &s->irq); 488380a37e4SHao Wu s->clock_in = qdev_init_clock_in(dev, "clock-in", npcm7xx_mft_update_clock, 489380a37e4SHao Wu s, ClockUpdate); 490380a37e4SHao Wu s->clock_1 = qdev_init_clock_out(dev, "clock1"); 491380a37e4SHao Wu s->clock_2 = qdev_init_clock_out(dev, "clock2"); 492380a37e4SHao Wu 493380a37e4SHao Wu for (int i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) { 494380a37e4SHao Wu object_property_add(obj, "max_rpm[*]", "uint32", 495380a37e4SHao Wu npcm7xx_mft_get_max_rpm, 496380a37e4SHao Wu npcm7xx_mft_set_max_rpm, 497380a37e4SHao Wu NULL, &s->max_rpm[i]); 498380a37e4SHao Wu } 499380a37e4SHao Wu qdev_init_gpio_in_named(dev, npcm7xx_mft_duty_handler, "duty", 500380a37e4SHao Wu NPCM7XX_MFT_FANIN_COUNT); 501380a37e4SHao Wu } 502380a37e4SHao Wu 503380a37e4SHao Wu static const VMStateDescription vmstate_npcm7xx_mft = { 504380a37e4SHao Wu .name = "npcm7xx-mft-module", 505380a37e4SHao Wu .version_id = 0, 506380a37e4SHao Wu .minimum_version_id = 0, 507e4ea952fSRichard Henderson .fields = (const VMStateField[]) { 508380a37e4SHao Wu VMSTATE_CLOCK(clock_in, NPCM7xxMFTState), 509380a37e4SHao Wu VMSTATE_CLOCK(clock_1, NPCM7xxMFTState), 510380a37e4SHao Wu VMSTATE_CLOCK(clock_2, NPCM7xxMFTState), 511380a37e4SHao Wu VMSTATE_UINT16_ARRAY(regs, NPCM7xxMFTState, NPCM7XX_MFT_NR_REGS), 512380a37e4SHao Wu VMSTATE_UINT32_ARRAY(max_rpm, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT), 513380a37e4SHao Wu VMSTATE_UINT32_ARRAY(duty, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT), 514380a37e4SHao Wu VMSTATE_END_OF_LIST(), 515380a37e4SHao Wu }, 516380a37e4SHao Wu }; 517380a37e4SHao Wu 518380a37e4SHao Wu static void npcm7xx_mft_class_init(ObjectClass *klass, void *data) 519380a37e4SHao Wu { 520380a37e4SHao Wu ResettableClass *rc = RESETTABLE_CLASS(klass); 521380a37e4SHao Wu DeviceClass *dc = DEVICE_CLASS(klass); 522380a37e4SHao Wu 523380a37e4SHao Wu dc->desc = "NPCM7xx MFT Controller"; 524380a37e4SHao Wu dc->vmsd = &vmstate_npcm7xx_mft; 525380a37e4SHao Wu rc->phases.enter = npcm7xx_mft_enter_reset; 526380a37e4SHao Wu rc->phases.hold = npcm7xx_mft_hold_reset; 527380a37e4SHao Wu } 528380a37e4SHao Wu 529380a37e4SHao Wu static const TypeInfo npcm7xx_mft_info = { 530380a37e4SHao Wu .name = TYPE_NPCM7XX_MFT, 531380a37e4SHao Wu .parent = TYPE_SYS_BUS_DEVICE, 532380a37e4SHao Wu .instance_size = sizeof(NPCM7xxMFTState), 533380a37e4SHao Wu .class_init = npcm7xx_mft_class_init, 534380a37e4SHao Wu .instance_init = npcm7xx_mft_init, 535380a37e4SHao Wu }; 536380a37e4SHao Wu 537380a37e4SHao Wu static void npcm7xx_mft_register_type(void) 538380a37e4SHao Wu { 539380a37e4SHao Wu type_register_static(&npcm7xx_mft_info); 540380a37e4SHao Wu } 541380a37e4SHao Wu type_init(npcm7xx_mft_register_type); 542