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
npcm7xx_mft_reset(NPCM7xxMFTState * s)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
npcm7xx_mft_clear_interrupt(NPCM7xxMFTState * s,uint8_t iclr)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 */
npcm7xx_mft_compare(int32_t src,uint16_t tgt,uint8_t cpcfg)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. */
npcm7xx_mft_compute_cnt(Clock * clock,uint32_t max_rpm,uint32_t duty,uint16_t tgt,uint8_t cpcfg,uint16_t * cnt)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 */
175593b910eSTigran Sogomonian count = clock_ns_to_ticks(clock,
176593b910eSTigran Sogomonian (uint64_t)(60 * NANOSECONDS_PER_SECOND) /
177593b910eSTigran 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 */
npcm7xx_mft_capture(NPCM7xxMFTState * s)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. */
npcm7xx_mft_update_clock(void * opaque,ClockEvent event)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
npcm7xx_mft_read(void * opaque,hwaddr offset,unsigned size)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
npcm7xx_mft_write(void * opaque,hwaddr offset,uint64_t v,unsigned size)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
npcm7xx_mft_check_mem_op(void * opaque,hwaddr offset,unsigned size,bool is_write,MemTxAttrs attrs)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
npcm7xx_mft_get_max_rpm(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)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
npcm7xx_mft_set_max_rpm(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)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
npcm7xx_mft_duty_handler(void * opaque,int n,int value)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
npcm7xx_mft_enter_reset(Object * obj,ResetType type)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
npcm7xx_mft_hold_reset(Object * obj,ResetType type)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
npcm7xx_mft_init(Object * obj)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
npcm7xx_mft_class_init(ObjectClass * klass,const void * data)518*12d1a768SPhilippe Mathieu-Daudé static void npcm7xx_mft_class_init(ObjectClass *klass, const 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
npcm7xx_mft_register_type(void)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