cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

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 */
     41REG16(NPCM7XX_MFT_CNT1, 0x00);
     42REG16(NPCM7XX_MFT_CRA, 0x02);
     43REG16(NPCM7XX_MFT_CRB, 0x04);
     44REG16(NPCM7XX_MFT_CNT2, 0x06);
     45REG16(NPCM7XX_MFT_PRSC, 0x08);
     46REG16(NPCM7XX_MFT_CKC, 0x0a);
     47REG16(NPCM7XX_MFT_MCTRL, 0x0c);
     48REG16(NPCM7XX_MFT_ICTRL, 0x0e);
     49REG16(NPCM7XX_MFT_ICLR, 0x10);
     50REG16(NPCM7XX_MFT_IEN, 0x12);
     51REG16(NPCM7XX_MFT_CPA, 0x14);
     52REG16(NPCM7XX_MFT_CPB, 0x16);
     53REG16(NPCM7XX_MFT_CPCFG, 0x18);
     54REG16(NPCM7XX_MFT_INASEL, 0x1a);
     55REG16(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
    104typedef 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
    113static 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
    123static 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 */
    139static 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. */
    155static 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 */
    210static 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. */
    314static 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
    345static 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
    365static 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
    389static 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
    421static 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
    427static 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
    442static 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
    451static 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
    463static void npcm7xx_mft_enter_reset(Object *obj, ResetType type)
    464{
    465    NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
    466
    467    npcm7xx_mft_reset(s);
    468}
    469
    470static void npcm7xx_mft_hold_reset(Object *obj)
    471{
    472    NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
    473
    474    qemu_irq_lower(s->irq);
    475}
    476
    477static 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
    502static 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
    517static 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
    528static 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
    536static void npcm7xx_mft_register_type(void)
    537{
    538    type_register_static(&npcm7xx_mft_info);
    539}
    540type_init(npcm7xx_mft_register_type);