qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

npcm7xx_mft.c (17625B)


      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, (60 * NANOSECONDS_PER_SECOND) /
    176             (rpm * NPCM7XX_MFT_PULSE_PER_REVOLUTION));
    177     }
    178 
    179     if (count > NPCM7XX_MFT_MAX_CNT) {
    180         count = -1;
    181     } else {
    182         /* The CNT is a countdown value from NPCM7XX_MFT_MAX_CNT. */
    183         count = NPCM7XX_MFT_MAX_CNT - count;
    184     }
    185     stopped = npcm7xx_mft_compare(count, tgt, cpcfg);
    186     if (stopped == -1) {
    187         if (count == -1) {
    188             /* Underflow */
    189             state = NPCM7XX_CAPTURE_UNDERFLOW;
    190         } else {
    191             state = NPCM7XX_CAPTURE_SUCCEED;
    192         }
    193     } else {
    194         count = stopped;
    195         state = NPCM7XX_CAPTURE_COMPARE_HIT;
    196     }
    197 
    198     if (count != -1) {
    199         *cnt = count;
    200     }
    201     trace_npcm7xx_mft_rpm(clock->canonical_path, clock_get_hz(clock),
    202                           state, count, rpm, duty);
    203     return state;
    204 }
    205 
    206 /*
    207  * Capture Fan RPM and update CNT and CR registers accordingly.
    208  * Raise IRQ if certain contidions are met in IEN.
    209  */
    210 static void npcm7xx_mft_capture(NPCM7xxMFTState *s)
    211 {
    212     int irq_level = 0;
    213     NPCM7xxMFTCaptureState state;
    214     int sel;
    215     uint8_t cpcfg;
    216 
    217     /*
    218      * If not mode 5, the behavior is undefined. We just do nothing in this
    219      * case.
    220      */
    221     if (!(s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_MODE5)) {
    222         return;
    223     }
    224 
    225     /* Capture input A. */
    226     if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TAEN &&
    227         s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) {
    228         sel = s->regs[R_NPCM7XX_MFT_INASEL] & NPCM7XX_MFT_INASEL_SELA;
    229         cpcfg = NPCM7XX_MFT_CPCFG_GET_A(s->regs[R_NPCM7XX_MFT_CPCFG]);
    230         state = npcm7xx_mft_compute_cnt(s->clock_1,
    231                                         sel ? s->max_rpm[2] : s->max_rpm[0],
    232                                         sel ? s->duty[2] : s->duty[0],
    233                                         s->regs[R_NPCM7XX_MFT_CPA],
    234                                         cpcfg,
    235                                         &s->regs[R_NPCM7XX_MFT_CNT1]);
    236         switch (state) {
    237         case NPCM7XX_CAPTURE_SUCCEED:
    238             /* Interrupt on input capture on TAn transition - TAPND */
    239             s->regs[R_NPCM7XX_MFT_CRA] = s->regs[R_NPCM7XX_MFT_CNT1];
    240             s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TAPND;
    241             if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TAIEN) {
    242                 irq_level = 1;
    243             }
    244             break;
    245 
    246         case NPCM7XX_CAPTURE_COMPARE_HIT:
    247             /* Compare Hit - TEPND */
    248             s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TEPND;
    249             if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TEIEN) {
    250                 irq_level = 1;
    251             }
    252             break;
    253 
    254         case NPCM7XX_CAPTURE_UNDERFLOW:
    255             /* Underflow - TCPND */
    256             s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TCPND;
    257             if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TCIEN) {
    258                 irq_level = 1;
    259             }
    260             break;
    261 
    262         default:
    263             g_assert_not_reached();
    264         }
    265     }
    266 
    267     /* Capture input B. */
    268     if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TBEN &&
    269         s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) {
    270         sel = s->regs[R_NPCM7XX_MFT_INBSEL] & NPCM7XX_MFT_INBSEL_SELB;
    271         cpcfg = NPCM7XX_MFT_CPCFG_GET_B(s->regs[R_NPCM7XX_MFT_CPCFG]);
    272         state = npcm7xx_mft_compute_cnt(s->clock_2,
    273                                         sel ? s->max_rpm[3] : s->max_rpm[1],
    274                                         sel ? s->duty[3] : s->duty[1],
    275                                         s->regs[R_NPCM7XX_MFT_CPB],
    276                                         cpcfg,
    277                                         &s->regs[R_NPCM7XX_MFT_CNT2]);
    278         switch (state) {
    279         case NPCM7XX_CAPTURE_SUCCEED:
    280             /* Interrupt on input capture on TBn transition - TBPND */
    281             s->regs[R_NPCM7XX_MFT_CRB] = s->regs[R_NPCM7XX_MFT_CNT2];
    282             s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TBPND;
    283             if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TBIEN) {
    284                 irq_level = 1;
    285             }
    286             break;
    287 
    288         case NPCM7XX_CAPTURE_COMPARE_HIT:
    289             /* Compare Hit - TFPND */
    290             s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TFPND;
    291             if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TFIEN) {
    292                 irq_level = 1;
    293             }
    294             break;
    295 
    296         case NPCM7XX_CAPTURE_UNDERFLOW:
    297             /* Underflow - TDPND */
    298             s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TDPND;
    299             if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TDIEN) {
    300                 irq_level = 1;
    301             }
    302             break;
    303 
    304         default:
    305             g_assert_not_reached();
    306         }
    307     }
    308 
    309     trace_npcm7xx_mft_capture(DEVICE(s)->canonical_path, irq_level);
    310     qemu_set_irq(s->irq, irq_level);
    311 }
    312 
    313 /* Update clock for counters. */
    314 static void npcm7xx_mft_update_clock(void *opaque, ClockEvent event)
    315 {
    316     NPCM7xxMFTState *s = NPCM7XX_MFT(opaque);
    317     uint64_t prescaled_clock_period;
    318 
    319     prescaled_clock_period = clock_get(s->clock_in) *
    320         (s->regs[R_NPCM7XX_MFT_PRSC] + 1ULL);
    321     trace_npcm7xx_mft_update_clock(s->clock_in->canonical_path,
    322                                    s->regs[R_NPCM7XX_MFT_CKC],
    323                                    clock_get(s->clock_in),
    324                                    prescaled_clock_period);
    325     /* Update clock 1 */
    326     if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) {
    327         /* Clock is prescaled. */
    328         clock_update(s->clock_1, prescaled_clock_period);
    329     } else {
    330         /* Clock stopped. */
    331         clock_update(s->clock_1, 0);
    332     }
    333     /* Update clock 2 */
    334     if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) {
    335         /* Clock is prescaled. */
    336         clock_update(s->clock_2, prescaled_clock_period);
    337     } else {
    338         /* Clock stopped. */
    339         clock_update(s->clock_2, 0);
    340     }
    341 
    342     npcm7xx_mft_capture(s);
    343 }
    344 
    345 static uint64_t npcm7xx_mft_read(void *opaque, hwaddr offset, unsigned size)
    346 {
    347     NPCM7xxMFTState *s = NPCM7XX_MFT(opaque);
    348     uint16_t value = 0;
    349 
    350     switch (offset) {
    351     case A_NPCM7XX_MFT_ICLR:
    352         qemu_log_mask(LOG_GUEST_ERROR,
    353                       "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n",
    354                       __func__, offset);
    355         break;
    356 
    357     default:
    358         value = s->regs[offset / 2];
    359     }
    360 
    361     trace_npcm7xx_mft_read(DEVICE(s)->canonical_path, offset, value);
    362     return value;
    363 }
    364 
    365 static void npcm7xx_mft_write(void *opaque, hwaddr offset,
    366                               uint64_t v, unsigned size)
    367 {
    368     NPCM7xxMFTState *s = NPCM7XX_MFT(opaque);
    369 
    370     trace_npcm7xx_mft_write(DEVICE(s)->canonical_path, offset, v);
    371     switch (offset) {
    372     case A_NPCM7XX_MFT_ICLR:
    373         npcm7xx_mft_clear_interrupt(s, v);
    374         break;
    375 
    376     case A_NPCM7XX_MFT_CKC:
    377     case A_NPCM7XX_MFT_PRSC:
    378         s->regs[offset / 2] = v;
    379         npcm7xx_mft_update_clock(s, ClockUpdate);
    380         break;
    381 
    382     default:
    383         s->regs[offset / 2] = v;
    384         npcm7xx_mft_capture(s);
    385         break;
    386     }
    387 }
    388 
    389 static bool npcm7xx_mft_check_mem_op(void *opaque, hwaddr offset,
    390                                      unsigned size, bool is_write,
    391                                      MemTxAttrs attrs)
    392 {
    393     switch (offset) {
    394     /* 16-bit registers. Must be accessed with 16-bit read/write.*/
    395     case A_NPCM7XX_MFT_CNT1:
    396     case A_NPCM7XX_MFT_CRA:
    397     case A_NPCM7XX_MFT_CRB:
    398     case A_NPCM7XX_MFT_CNT2:
    399     case A_NPCM7XX_MFT_CPA:
    400     case A_NPCM7XX_MFT_CPB:
    401         return size == 2;
    402 
    403     /* 8-bit registers. Must be accessed with 8-bit read/write.*/
    404     case A_NPCM7XX_MFT_PRSC:
    405     case A_NPCM7XX_MFT_CKC:
    406     case A_NPCM7XX_MFT_MCTRL:
    407     case A_NPCM7XX_MFT_ICTRL:
    408     case A_NPCM7XX_MFT_ICLR:
    409     case A_NPCM7XX_MFT_IEN:
    410     case A_NPCM7XX_MFT_CPCFG:
    411     case A_NPCM7XX_MFT_INASEL:
    412     case A_NPCM7XX_MFT_INBSEL:
    413         return size == 1;
    414 
    415     default:
    416         /* Invalid registers. */
    417         return false;
    418     }
    419 }
    420 
    421 static void npcm7xx_mft_get_max_rpm(Object *obj, Visitor *v, const char *name,
    422                                     void *opaque, Error **errp)
    423 {
    424     visit_type_uint32(v, name, (uint32_t *)opaque, errp);
    425 }
    426 
    427 static void npcm7xx_mft_set_max_rpm(Object *obj, Visitor *v, const char *name,
    428                                     void *opaque, Error **errp)
    429 {
    430     NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
    431     uint32_t *max_rpm = opaque;
    432     uint32_t value;
    433 
    434     if (!visit_type_uint32(v, name, &value, errp)) {
    435         return;
    436     }
    437 
    438     *max_rpm = value;
    439     npcm7xx_mft_capture(s);
    440 }
    441 
    442 static void npcm7xx_mft_duty_handler(void *opaque, int n, int value)
    443 {
    444     NPCM7xxMFTState *s = NPCM7XX_MFT(opaque);
    445 
    446     trace_npcm7xx_mft_set_duty(DEVICE(s)->canonical_path, n, value);
    447     s->duty[n] = value;
    448     npcm7xx_mft_capture(s);
    449 }
    450 
    451 static const struct MemoryRegionOps npcm7xx_mft_ops = {
    452     .read       = npcm7xx_mft_read,
    453     .write      = npcm7xx_mft_write,
    454     .endianness = DEVICE_LITTLE_ENDIAN,
    455     .valid      = {
    456         .min_access_size        = 1,
    457         .max_access_size        = 2,
    458         .unaligned              = false,
    459         .accepts                = npcm7xx_mft_check_mem_op,
    460     },
    461 };
    462 
    463 static void npcm7xx_mft_enter_reset(Object *obj, ResetType type)
    464 {
    465     NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
    466 
    467     npcm7xx_mft_reset(s);
    468 }
    469 
    470 static void npcm7xx_mft_hold_reset(Object *obj)
    471 {
    472     NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
    473 
    474     qemu_irq_lower(s->irq);
    475 }
    476 
    477 static void npcm7xx_mft_init(Object *obj)
    478 {
    479     NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
    480     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    481     DeviceState *dev = DEVICE(obj);
    482 
    483     memory_region_init_io(&s->iomem, obj, &npcm7xx_mft_ops, s,
    484                           TYPE_NPCM7XX_MFT, 4 * KiB);
    485     sysbus_init_mmio(sbd, &s->iomem);
    486     sysbus_init_irq(sbd, &s->irq);
    487     s->clock_in = qdev_init_clock_in(dev, "clock-in", npcm7xx_mft_update_clock,
    488                                      s, ClockUpdate);
    489     s->clock_1 = qdev_init_clock_out(dev, "clock1");
    490     s->clock_2 = qdev_init_clock_out(dev, "clock2");
    491 
    492     for (int i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
    493         object_property_add(obj, "max_rpm[*]", "uint32",
    494                             npcm7xx_mft_get_max_rpm,
    495                             npcm7xx_mft_set_max_rpm,
    496                             NULL, &s->max_rpm[i]);
    497     }
    498     qdev_init_gpio_in_named(dev, npcm7xx_mft_duty_handler, "duty",
    499                             NPCM7XX_MFT_FANIN_COUNT);
    500 }
    501 
    502 static const VMStateDescription vmstate_npcm7xx_mft = {
    503     .name = "npcm7xx-mft-module",
    504     .version_id = 0,
    505     .minimum_version_id = 0,
    506     .fields = (VMStateField[]) {
    507         VMSTATE_CLOCK(clock_in, NPCM7xxMFTState),
    508         VMSTATE_CLOCK(clock_1, NPCM7xxMFTState),
    509         VMSTATE_CLOCK(clock_2, NPCM7xxMFTState),
    510         VMSTATE_UINT16_ARRAY(regs, NPCM7xxMFTState, NPCM7XX_MFT_NR_REGS),
    511         VMSTATE_UINT32_ARRAY(max_rpm, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT),
    512         VMSTATE_UINT32_ARRAY(duty, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT),
    513         VMSTATE_END_OF_LIST(),
    514     },
    515 };
    516 
    517 static void npcm7xx_mft_class_init(ObjectClass *klass, void *data)
    518 {
    519     ResettableClass *rc = RESETTABLE_CLASS(klass);
    520     DeviceClass *dc = DEVICE_CLASS(klass);
    521 
    522     dc->desc = "NPCM7xx MFT Controller";
    523     dc->vmsd = &vmstate_npcm7xx_mft;
    524     rc->phases.enter = npcm7xx_mft_enter_reset;
    525     rc->phases.hold = npcm7xx_mft_hold_reset;
    526 }
    527 
    528 static const TypeInfo npcm7xx_mft_info = {
    529     .name               = TYPE_NPCM7XX_MFT,
    530     .parent             = TYPE_SYS_BUS_DEVICE,
    531     .instance_size      = sizeof(NPCM7xxMFTState),
    532     .class_init         = npcm7xx_mft_class_init,
    533     .instance_init      = npcm7xx_mft_init,
    534 };
    535 
    536 static void npcm7xx_mft_register_type(void)
    537 {
    538     type_register_static(&npcm7xx_mft_info);
    539 }
    540 type_init(npcm7xx_mft_register_type);