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