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 */ 175380a37e4SHao Wu count = clock_ns_to_ticks(clock, (60 * NANOSECONDS_PER_SECOND) / 176380a37e4SHao Wu (rpm * NPCM7XX_MFT_PULSE_PER_REVOLUTION)); 177380a37e4SHao Wu } 178380a37e4SHao Wu 179380a37e4SHao Wu if (count > NPCM7XX_MFT_MAX_CNT) { 180380a37e4SHao Wu count = -1; 181380a37e4SHao Wu } else { 182380a37e4SHao Wu /* The CNT is a countdown value from NPCM7XX_MFT_MAX_CNT. */ 183380a37e4SHao Wu count = NPCM7XX_MFT_MAX_CNT - count; 184380a37e4SHao Wu } 185380a37e4SHao Wu stopped = npcm7xx_mft_compare(count, tgt, cpcfg); 186380a37e4SHao Wu if (stopped == -1) { 187380a37e4SHao Wu if (count == -1) { 188380a37e4SHao Wu /* Underflow */ 189380a37e4SHao Wu state = NPCM7XX_CAPTURE_UNDERFLOW; 190380a37e4SHao Wu } else { 191380a37e4SHao Wu state = NPCM7XX_CAPTURE_SUCCEED; 192380a37e4SHao Wu } 193380a37e4SHao Wu } else { 194380a37e4SHao Wu count = stopped; 195380a37e4SHao Wu state = NPCM7XX_CAPTURE_COMPARE_HIT; 196380a37e4SHao Wu } 197380a37e4SHao Wu 198380a37e4SHao Wu if (count != -1) { 199380a37e4SHao Wu *cnt = count; 200380a37e4SHao Wu } 201380a37e4SHao Wu trace_npcm7xx_mft_rpm(clock->canonical_path, clock_get_hz(clock), 202380a37e4SHao Wu state, count, rpm, duty); 203380a37e4SHao Wu return state; 204380a37e4SHao Wu } 205380a37e4SHao Wu 206380a37e4SHao Wu /* 207380a37e4SHao Wu * Capture Fan RPM and update CNT and CR registers accordingly. 208380a37e4SHao Wu * Raise IRQ if certain contidions are met in IEN. 209380a37e4SHao Wu */ 210380a37e4SHao Wu static void npcm7xx_mft_capture(NPCM7xxMFTState *s) 211380a37e4SHao Wu { 212380a37e4SHao Wu int irq_level = 0; 213380a37e4SHao Wu NPCM7xxMFTCaptureState state; 214380a37e4SHao Wu int sel; 215380a37e4SHao Wu uint8_t cpcfg; 216380a37e4SHao Wu 217380a37e4SHao Wu /* 218380a37e4SHao Wu * If not mode 5, the behavior is undefined. We just do nothing in this 219380a37e4SHao Wu * case. 220380a37e4SHao Wu */ 221380a37e4SHao Wu if (!(s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_MODE5)) { 222380a37e4SHao Wu return; 223380a37e4SHao Wu } 224380a37e4SHao Wu 225380a37e4SHao Wu /* Capture input A. */ 226380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TAEN && 227380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) { 228380a37e4SHao Wu sel = s->regs[R_NPCM7XX_MFT_INASEL] & NPCM7XX_MFT_INASEL_SELA; 229380a37e4SHao Wu cpcfg = NPCM7XX_MFT_CPCFG_GET_A(s->regs[R_NPCM7XX_MFT_CPCFG]); 230380a37e4SHao Wu state = npcm7xx_mft_compute_cnt(s->clock_1, 231380a37e4SHao Wu sel ? s->max_rpm[2] : s->max_rpm[0], 232380a37e4SHao Wu sel ? s->duty[2] : s->duty[0], 233380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CPA], 234380a37e4SHao Wu cpcfg, 235380a37e4SHao Wu &s->regs[R_NPCM7XX_MFT_CNT1]); 236380a37e4SHao Wu switch (state) { 237380a37e4SHao Wu case NPCM7XX_CAPTURE_SUCCEED: 238380a37e4SHao Wu /* Interrupt on input capture on TAn transition - TAPND */ 239380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CRA] = s->regs[R_NPCM7XX_MFT_CNT1]; 240380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TAPND; 241380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TAIEN) { 242380a37e4SHao Wu irq_level = 1; 243380a37e4SHao Wu } 244380a37e4SHao Wu break; 245380a37e4SHao Wu 246380a37e4SHao Wu case NPCM7XX_CAPTURE_COMPARE_HIT: 247380a37e4SHao Wu /* Compare Hit - TEPND */ 248380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TEPND; 249380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TEIEN) { 250380a37e4SHao Wu irq_level = 1; 251380a37e4SHao Wu } 252380a37e4SHao Wu break; 253380a37e4SHao Wu 254380a37e4SHao Wu case NPCM7XX_CAPTURE_UNDERFLOW: 255380a37e4SHao Wu /* Underflow - TCPND */ 256380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TCPND; 257380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TCIEN) { 258380a37e4SHao Wu irq_level = 1; 259380a37e4SHao Wu } 260380a37e4SHao Wu break; 261380a37e4SHao Wu 262380a37e4SHao Wu default: 263380a37e4SHao Wu g_assert_not_reached(); 264380a37e4SHao Wu } 265380a37e4SHao Wu } 266380a37e4SHao Wu 267380a37e4SHao Wu /* Capture input B. */ 268380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TBEN && 269380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) { 270380a37e4SHao Wu sel = s->regs[R_NPCM7XX_MFT_INBSEL] & NPCM7XX_MFT_INBSEL_SELB; 271380a37e4SHao Wu cpcfg = NPCM7XX_MFT_CPCFG_GET_B(s->regs[R_NPCM7XX_MFT_CPCFG]); 272380a37e4SHao Wu state = npcm7xx_mft_compute_cnt(s->clock_2, 273380a37e4SHao Wu sel ? s->max_rpm[3] : s->max_rpm[1], 274380a37e4SHao Wu sel ? s->duty[3] : s->duty[1], 275380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CPB], 276380a37e4SHao Wu cpcfg, 277380a37e4SHao Wu &s->regs[R_NPCM7XX_MFT_CNT2]); 278380a37e4SHao Wu switch (state) { 279380a37e4SHao Wu case NPCM7XX_CAPTURE_SUCCEED: 280380a37e4SHao Wu /* Interrupt on input capture on TBn transition - TBPND */ 281380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CRB] = s->regs[R_NPCM7XX_MFT_CNT2]; 282380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TBPND; 283380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TBIEN) { 284380a37e4SHao Wu irq_level = 1; 285380a37e4SHao Wu } 286380a37e4SHao Wu break; 287380a37e4SHao Wu 288380a37e4SHao Wu case NPCM7XX_CAPTURE_COMPARE_HIT: 289380a37e4SHao Wu /* Compare Hit - TFPND */ 290380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TFPND; 291380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TFIEN) { 292380a37e4SHao Wu irq_level = 1; 293380a37e4SHao Wu } 294380a37e4SHao Wu break; 295380a37e4SHao Wu 296380a37e4SHao Wu case NPCM7XX_CAPTURE_UNDERFLOW: 297380a37e4SHao Wu /* Underflow - TDPND */ 298380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TDPND; 299380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TDIEN) { 300380a37e4SHao Wu irq_level = 1; 301380a37e4SHao Wu } 302380a37e4SHao Wu break; 303380a37e4SHao Wu 304380a37e4SHao Wu default: 305380a37e4SHao Wu g_assert_not_reached(); 306380a37e4SHao Wu } 307380a37e4SHao Wu } 308380a37e4SHao Wu 309380a37e4SHao Wu trace_npcm7xx_mft_capture(DEVICE(s)->canonical_path, irq_level); 310380a37e4SHao Wu qemu_set_irq(s->irq, irq_level); 311380a37e4SHao Wu } 312380a37e4SHao Wu 313380a37e4SHao Wu /* Update clock for counters. */ 314380a37e4SHao Wu static void npcm7xx_mft_update_clock(void *opaque, ClockEvent event) 315380a37e4SHao Wu { 316380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 317380a37e4SHao Wu uint64_t prescaled_clock_period; 318380a37e4SHao Wu 319380a37e4SHao Wu prescaled_clock_period = clock_get(s->clock_in) * 320380a37e4SHao Wu (s->regs[R_NPCM7XX_MFT_PRSC] + 1ULL); 321380a37e4SHao Wu trace_npcm7xx_mft_update_clock(s->clock_in->canonical_path, 322380a37e4SHao Wu s->regs[R_NPCM7XX_MFT_CKC], 323380a37e4SHao Wu clock_get(s->clock_in), 324380a37e4SHao Wu prescaled_clock_period); 325380a37e4SHao Wu /* Update clock 1 */ 326380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) { 327380a37e4SHao Wu /* Clock is prescaled. */ 328380a37e4SHao Wu clock_update(s->clock_1, prescaled_clock_period); 329380a37e4SHao Wu } else { 330380a37e4SHao Wu /* Clock stopped. */ 331380a37e4SHao Wu clock_update(s->clock_1, 0); 332380a37e4SHao Wu } 333380a37e4SHao Wu /* Update clock 2 */ 334380a37e4SHao Wu if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) { 335380a37e4SHao Wu /* Clock is prescaled. */ 336380a37e4SHao Wu clock_update(s->clock_2, prescaled_clock_period); 337380a37e4SHao Wu } else { 338380a37e4SHao Wu /* Clock stopped. */ 339380a37e4SHao Wu clock_update(s->clock_2, 0); 340380a37e4SHao Wu } 341380a37e4SHao Wu 342380a37e4SHao Wu npcm7xx_mft_capture(s); 343380a37e4SHao Wu } 344380a37e4SHao Wu 345380a37e4SHao Wu static uint64_t npcm7xx_mft_read(void *opaque, hwaddr offset, unsigned size) 346380a37e4SHao Wu { 347380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 348380a37e4SHao Wu uint16_t value = 0; 349380a37e4SHao Wu 350380a37e4SHao Wu switch (offset) { 351380a37e4SHao Wu case A_NPCM7XX_MFT_ICLR: 352380a37e4SHao Wu qemu_log_mask(LOG_GUEST_ERROR, 353380a37e4SHao Wu "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n", 354380a37e4SHao Wu __func__, offset); 355380a37e4SHao Wu break; 356380a37e4SHao Wu 357380a37e4SHao Wu default: 358380a37e4SHao Wu value = s->regs[offset / 2]; 359380a37e4SHao Wu } 360380a37e4SHao Wu 361380a37e4SHao Wu trace_npcm7xx_mft_read(DEVICE(s)->canonical_path, offset, value); 362380a37e4SHao Wu return value; 363380a37e4SHao Wu } 364380a37e4SHao Wu 365380a37e4SHao Wu static void npcm7xx_mft_write(void *opaque, hwaddr offset, 366380a37e4SHao Wu uint64_t v, unsigned size) 367380a37e4SHao Wu { 368380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 369380a37e4SHao Wu 370380a37e4SHao Wu trace_npcm7xx_mft_write(DEVICE(s)->canonical_path, offset, v); 371380a37e4SHao Wu switch (offset) { 372380a37e4SHao Wu case A_NPCM7XX_MFT_ICLR: 373380a37e4SHao Wu npcm7xx_mft_clear_interrupt(s, v); 374380a37e4SHao Wu break; 375380a37e4SHao Wu 376380a37e4SHao Wu case A_NPCM7XX_MFT_CKC: 377380a37e4SHao Wu case A_NPCM7XX_MFT_PRSC: 378380a37e4SHao Wu s->regs[offset / 2] = v; 379380a37e4SHao Wu npcm7xx_mft_update_clock(s, ClockUpdate); 380380a37e4SHao Wu break; 381380a37e4SHao Wu 382380a37e4SHao Wu default: 383380a37e4SHao Wu s->regs[offset / 2] = v; 384380a37e4SHao Wu npcm7xx_mft_capture(s); 385380a37e4SHao Wu break; 386380a37e4SHao Wu } 387380a37e4SHao Wu } 388380a37e4SHao Wu 389380a37e4SHao Wu static bool npcm7xx_mft_check_mem_op(void *opaque, hwaddr offset, 390380a37e4SHao Wu unsigned size, bool is_write, 391380a37e4SHao Wu MemTxAttrs attrs) 392380a37e4SHao Wu { 393380a37e4SHao Wu switch (offset) { 394380a37e4SHao Wu /* 16-bit registers. Must be accessed with 16-bit read/write.*/ 395380a37e4SHao Wu case A_NPCM7XX_MFT_CNT1: 396380a37e4SHao Wu case A_NPCM7XX_MFT_CRA: 397380a37e4SHao Wu case A_NPCM7XX_MFT_CRB: 398380a37e4SHao Wu case A_NPCM7XX_MFT_CNT2: 399380a37e4SHao Wu case A_NPCM7XX_MFT_CPA: 400380a37e4SHao Wu case A_NPCM7XX_MFT_CPB: 401380a37e4SHao Wu return size == 2; 402380a37e4SHao Wu 403380a37e4SHao Wu /* 8-bit registers. Must be accessed with 8-bit read/write.*/ 404380a37e4SHao Wu case A_NPCM7XX_MFT_PRSC: 405380a37e4SHao Wu case A_NPCM7XX_MFT_CKC: 406380a37e4SHao Wu case A_NPCM7XX_MFT_MCTRL: 407380a37e4SHao Wu case A_NPCM7XX_MFT_ICTRL: 408380a37e4SHao Wu case A_NPCM7XX_MFT_ICLR: 409380a37e4SHao Wu case A_NPCM7XX_MFT_IEN: 410380a37e4SHao Wu case A_NPCM7XX_MFT_CPCFG: 411380a37e4SHao Wu case A_NPCM7XX_MFT_INASEL: 412380a37e4SHao Wu case A_NPCM7XX_MFT_INBSEL: 413380a37e4SHao Wu return size == 1; 414380a37e4SHao Wu 415380a37e4SHao Wu default: 416380a37e4SHao Wu /* Invalid registers. */ 417380a37e4SHao Wu return false; 418380a37e4SHao Wu } 419380a37e4SHao Wu } 420380a37e4SHao Wu 421380a37e4SHao Wu static void npcm7xx_mft_get_max_rpm(Object *obj, Visitor *v, const char *name, 422380a37e4SHao Wu void *opaque, Error **errp) 423380a37e4SHao Wu { 424380a37e4SHao Wu visit_type_uint32(v, name, (uint32_t *)opaque, errp); 425380a37e4SHao Wu } 426380a37e4SHao Wu 427380a37e4SHao Wu static void npcm7xx_mft_set_max_rpm(Object *obj, Visitor *v, const char *name, 428380a37e4SHao Wu void *opaque, Error **errp) 429380a37e4SHao Wu { 430380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 431380a37e4SHao Wu uint32_t *max_rpm = opaque; 432380a37e4SHao Wu uint32_t value; 433380a37e4SHao Wu 434380a37e4SHao Wu if (!visit_type_uint32(v, name, &value, errp)) { 435380a37e4SHao Wu return; 436380a37e4SHao Wu } 437380a37e4SHao Wu 438380a37e4SHao Wu *max_rpm = value; 439380a37e4SHao Wu npcm7xx_mft_capture(s); 440380a37e4SHao Wu } 441380a37e4SHao Wu 442380a37e4SHao Wu static void npcm7xx_mft_duty_handler(void *opaque, int n, int value) 443380a37e4SHao Wu { 444380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 445380a37e4SHao Wu 446380a37e4SHao Wu trace_npcm7xx_mft_set_duty(DEVICE(s)->canonical_path, n, value); 447380a37e4SHao Wu s->duty[n] = value; 448380a37e4SHao Wu npcm7xx_mft_capture(s); 449380a37e4SHao Wu } 450380a37e4SHao Wu 451380a37e4SHao Wu static const struct MemoryRegionOps npcm7xx_mft_ops = { 452380a37e4SHao Wu .read = npcm7xx_mft_read, 453380a37e4SHao Wu .write = npcm7xx_mft_write, 454380a37e4SHao Wu .endianness = DEVICE_LITTLE_ENDIAN, 455380a37e4SHao Wu .valid = { 456380a37e4SHao Wu .min_access_size = 1, 457380a37e4SHao Wu .max_access_size = 2, 458380a37e4SHao Wu .unaligned = false, 459380a37e4SHao Wu .accepts = npcm7xx_mft_check_mem_op, 460380a37e4SHao Wu }, 461380a37e4SHao Wu }; 462380a37e4SHao Wu 463380a37e4SHao Wu static void npcm7xx_mft_enter_reset(Object *obj, ResetType type) 464380a37e4SHao Wu { 465380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 466380a37e4SHao Wu 467380a37e4SHao Wu npcm7xx_mft_reset(s); 468380a37e4SHao Wu } 469380a37e4SHao Wu 470380a37e4SHao Wu static void npcm7xx_mft_hold_reset(Object *obj) 471380a37e4SHao Wu { 472380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 473380a37e4SHao Wu 474380a37e4SHao Wu qemu_irq_lower(s->irq); 475380a37e4SHao Wu } 476380a37e4SHao Wu 477380a37e4SHao Wu static void npcm7xx_mft_init(Object *obj) 478380a37e4SHao Wu { 479380a37e4SHao Wu NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 480380a37e4SHao Wu SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 481380a37e4SHao Wu DeviceState *dev = DEVICE(obj); 482380a37e4SHao Wu 483380a37e4SHao Wu memory_region_init_io(&s->iomem, obj, &npcm7xx_mft_ops, s, 484380a37e4SHao Wu TYPE_NPCM7XX_MFT, 4 * KiB); 485380a37e4SHao Wu sysbus_init_mmio(sbd, &s->iomem); 486380a37e4SHao Wu sysbus_init_irq(sbd, &s->irq); 487380a37e4SHao Wu s->clock_in = qdev_init_clock_in(dev, "clock-in", npcm7xx_mft_update_clock, 488380a37e4SHao Wu s, ClockUpdate); 489380a37e4SHao Wu s->clock_1 = qdev_init_clock_out(dev, "clock1"); 490380a37e4SHao Wu s->clock_2 = qdev_init_clock_out(dev, "clock2"); 491380a37e4SHao Wu 492380a37e4SHao Wu for (int i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) { 493380a37e4SHao Wu object_property_add(obj, "max_rpm[*]", "uint32", 494380a37e4SHao Wu npcm7xx_mft_get_max_rpm, 495380a37e4SHao Wu npcm7xx_mft_set_max_rpm, 496380a37e4SHao Wu NULL, &s->max_rpm[i]); 497380a37e4SHao Wu } 498380a37e4SHao Wu qdev_init_gpio_in_named(dev, npcm7xx_mft_duty_handler, "duty", 499380a37e4SHao Wu NPCM7XX_MFT_FANIN_COUNT); 500380a37e4SHao Wu } 501380a37e4SHao Wu 502380a37e4SHao Wu static const VMStateDescription vmstate_npcm7xx_mft = { 503380a37e4SHao Wu .name = "npcm7xx-mft-module", 504380a37e4SHao Wu .version_id = 0, 505380a37e4SHao Wu .minimum_version_id = 0, 506*e4ea952fSRichard Henderson .fields = (const VMStateField[]) { 507380a37e4SHao Wu VMSTATE_CLOCK(clock_in, NPCM7xxMFTState), 508380a37e4SHao Wu VMSTATE_CLOCK(clock_1, NPCM7xxMFTState), 509380a37e4SHao Wu VMSTATE_CLOCK(clock_2, NPCM7xxMFTState), 510380a37e4SHao Wu VMSTATE_UINT16_ARRAY(regs, NPCM7xxMFTState, NPCM7XX_MFT_NR_REGS), 511380a37e4SHao Wu VMSTATE_UINT32_ARRAY(max_rpm, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT), 512380a37e4SHao Wu VMSTATE_UINT32_ARRAY(duty, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT), 513380a37e4SHao Wu VMSTATE_END_OF_LIST(), 514380a37e4SHao Wu }, 515380a37e4SHao Wu }; 516380a37e4SHao Wu 517380a37e4SHao Wu static void npcm7xx_mft_class_init(ObjectClass *klass, void *data) 518380a37e4SHao Wu { 519380a37e4SHao Wu ResettableClass *rc = RESETTABLE_CLASS(klass); 520380a37e4SHao Wu DeviceClass *dc = DEVICE_CLASS(klass); 521380a37e4SHao Wu 522380a37e4SHao Wu dc->desc = "NPCM7xx MFT Controller"; 523380a37e4SHao Wu dc->vmsd = &vmstate_npcm7xx_mft; 524380a37e4SHao Wu rc->phases.enter = npcm7xx_mft_enter_reset; 525380a37e4SHao Wu rc->phases.hold = npcm7xx_mft_hold_reset; 526380a37e4SHao Wu } 527380a37e4SHao Wu 528380a37e4SHao Wu static const TypeInfo npcm7xx_mft_info = { 529380a37e4SHao Wu .name = TYPE_NPCM7XX_MFT, 530380a37e4SHao Wu .parent = TYPE_SYS_BUS_DEVICE, 531380a37e4SHao Wu .instance_size = sizeof(NPCM7xxMFTState), 532380a37e4SHao Wu .class_init = npcm7xx_mft_class_init, 533380a37e4SHao Wu .instance_init = npcm7xx_mft_init, 534380a37e4SHao Wu }; 535380a37e4SHao Wu 536380a37e4SHao Wu static void npcm7xx_mft_register_type(void) 537380a37e4SHao Wu { 538380a37e4SHao Wu type_register_static(&npcm7xx_mft_info); 539380a37e4SHao Wu } 540380a37e4SHao Wu type_init(npcm7xx_mft_register_type); 541