xref: /qemu/hw/misc/npcm7xx_mft.c (revision 380a37e4)
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