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_clk.c (33230B)


      1/*
      2 * Nuvoton NPCM7xx Clock Control Registers.
      3 *
      4 * Copyright 2020 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
     19#include "hw/misc/npcm7xx_clk.h"
     20#include "hw/timer/npcm7xx_timer.h"
     21#include "hw/qdev-clock.h"
     22#include "migration/vmstate.h"
     23#include "qemu/error-report.h"
     24#include "qemu/log.h"
     25#include "qemu/module.h"
     26#include "qemu/timer.h"
     27#include "qemu/units.h"
     28#include "trace.h"
     29#include "sysemu/watchdog.h"
     30
     31/*
     32 * The reference clock hz, and the SECCNT and CNTR25M registers in this module,
     33 * is always 25 MHz.
     34 */
     35#define NPCM7XX_CLOCK_REF_HZ            (25000000)
     36
     37/* Register Field Definitions */
     38#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex-A9 Cores */
     39
     40#define PLLCON_LOKI     BIT(31)
     41#define PLLCON_LOKS     BIT(30)
     42#define PLLCON_PWDEN    BIT(12)
     43#define PLLCON_FBDV(con) extract32((con), 16, 12)
     44#define PLLCON_OTDV2(con) extract32((con), 13, 3)
     45#define PLLCON_OTDV1(con) extract32((con), 8, 3)
     46#define PLLCON_INDV(con) extract32((con), 0, 6)
     47
     48enum NPCM7xxCLKRegisters {
     49    NPCM7XX_CLK_CLKEN1,
     50    NPCM7XX_CLK_CLKSEL,
     51    NPCM7XX_CLK_CLKDIV1,
     52    NPCM7XX_CLK_PLLCON0,
     53    NPCM7XX_CLK_PLLCON1,
     54    NPCM7XX_CLK_SWRSTR,
     55    NPCM7XX_CLK_IPSRST1         = 0x20 / sizeof(uint32_t),
     56    NPCM7XX_CLK_IPSRST2,
     57    NPCM7XX_CLK_CLKEN2,
     58    NPCM7XX_CLK_CLKDIV2,
     59    NPCM7XX_CLK_CLKEN3,
     60    NPCM7XX_CLK_IPSRST3,
     61    NPCM7XX_CLK_WD0RCR,
     62    NPCM7XX_CLK_WD1RCR,
     63    NPCM7XX_CLK_WD2RCR,
     64    NPCM7XX_CLK_SWRSTC1,
     65    NPCM7XX_CLK_SWRSTC2,
     66    NPCM7XX_CLK_SWRSTC3,
     67    NPCM7XX_CLK_SWRSTC4,
     68    NPCM7XX_CLK_PLLCON2,
     69    NPCM7XX_CLK_CLKDIV3,
     70    NPCM7XX_CLK_CORSTC,
     71    NPCM7XX_CLK_PLLCONG,
     72    NPCM7XX_CLK_AHBCKFI,
     73    NPCM7XX_CLK_SECCNT,
     74    NPCM7XX_CLK_CNTR25M,
     75    NPCM7XX_CLK_REGS_END,
     76};
     77
     78/*
     79 * These reset values were taken from version 0.91 of the NPCM750R data sheet.
     80 *
     81 * All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on
     82 * core domain reset, but this reset type is not yet supported by QEMU.
     83 */
     84static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
     85    [NPCM7XX_CLK_CLKEN1]        = 0xffffffff,
     86    [NPCM7XX_CLK_CLKSEL]        = 0x004aaaaa,
     87    [NPCM7XX_CLK_CLKDIV1]       = 0x5413f855,
     88    [NPCM7XX_CLK_PLLCON0]       = 0x00222101 | PLLCON_LOKI,
     89    [NPCM7XX_CLK_PLLCON1]       = 0x00202101 | PLLCON_LOKI,
     90    [NPCM7XX_CLK_IPSRST1]       = 0x00001000,
     91    [NPCM7XX_CLK_IPSRST2]       = 0x80000000,
     92    [NPCM7XX_CLK_CLKEN2]        = 0xffffffff,
     93    [NPCM7XX_CLK_CLKDIV2]       = 0xaa4f8f9f,
     94    [NPCM7XX_CLK_CLKEN3]        = 0xffffffff,
     95    [NPCM7XX_CLK_IPSRST3]       = 0x03000000,
     96    [NPCM7XX_CLK_WD0RCR]        = 0xffffffff,
     97    [NPCM7XX_CLK_WD1RCR]        = 0xffffffff,
     98    [NPCM7XX_CLK_WD2RCR]        = 0xffffffff,
     99    [NPCM7XX_CLK_SWRSTC1]       = 0x00000003,
    100    [NPCM7XX_CLK_PLLCON2]       = 0x00c02105 | PLLCON_LOKI,
    101    [NPCM7XX_CLK_CORSTC]        = 0x04000003,
    102    [NPCM7XX_CLK_PLLCONG]       = 0x01228606 | PLLCON_LOKI,
    103    [NPCM7XX_CLK_AHBCKFI]       = 0x000000c8,
    104};
    105
    106/* The number of watchdogs that can trigger a reset. */
    107#define NPCM7XX_NR_WATCHDOGS    (3)
    108
    109/* Clock converter functions */
    110
    111#define TYPE_NPCM7XX_CLOCK_PLL "npcm7xx-clock-pll"
    112#define NPCM7XX_CLOCK_PLL(obj) OBJECT_CHECK(NPCM7xxClockPLLState, \
    113        (obj), TYPE_NPCM7XX_CLOCK_PLL)
    114#define TYPE_NPCM7XX_CLOCK_SEL "npcm7xx-clock-sel"
    115#define NPCM7XX_CLOCK_SEL(obj) OBJECT_CHECK(NPCM7xxClockSELState, \
    116        (obj), TYPE_NPCM7XX_CLOCK_SEL)
    117#define TYPE_NPCM7XX_CLOCK_DIVIDER "npcm7xx-clock-divider"
    118#define NPCM7XX_CLOCK_DIVIDER(obj) OBJECT_CHECK(NPCM7xxClockDividerState, \
    119        (obj), TYPE_NPCM7XX_CLOCK_DIVIDER)
    120
    121static void npcm7xx_clk_update_pll(void *opaque)
    122{
    123    NPCM7xxClockPLLState *s = opaque;
    124    uint32_t con = s->clk->regs[s->reg];
    125    uint64_t freq;
    126
    127    /* The PLL is grounded if it is not locked yet. */
    128    if (con & PLLCON_LOKI) {
    129        freq = clock_get_hz(s->clock_in);
    130        freq *= PLLCON_FBDV(con);
    131        freq /= PLLCON_INDV(con) * PLLCON_OTDV1(con) * PLLCON_OTDV2(con);
    132    } else {
    133        freq = 0;
    134    }
    135
    136    clock_update_hz(s->clock_out, freq);
    137}
    138
    139static void npcm7xx_clk_update_sel(void *opaque)
    140{
    141    NPCM7xxClockSELState *s = opaque;
    142    uint32_t index = extract32(s->clk->regs[NPCM7XX_CLK_CLKSEL], s->offset,
    143            s->len);
    144
    145    if (index >= s->input_size) {
    146        qemu_log_mask(LOG_GUEST_ERROR,
    147                      "%s: SEL index: %u out of range\n",
    148                      __func__, index);
    149        index = 0;
    150    }
    151    clock_update_hz(s->clock_out, clock_get_hz(s->clock_in[index]));
    152}
    153
    154static void npcm7xx_clk_update_divider(void *opaque)
    155{
    156    NPCM7xxClockDividerState *s = opaque;
    157    uint32_t freq;
    158
    159    freq = s->divide(s);
    160    clock_update_hz(s->clock_out, freq);
    161}
    162
    163static uint32_t divide_by_constant(NPCM7xxClockDividerState *s)
    164{
    165    return clock_get_hz(s->clock_in) / s->divisor;
    166}
    167
    168static uint32_t divide_by_reg_divisor(NPCM7xxClockDividerState *s)
    169{
    170    return clock_get_hz(s->clock_in) /
    171            (extract32(s->clk->regs[s->reg], s->offset, s->len) + 1);
    172}
    173
    174static uint32_t divide_by_reg_divisor_times_2(NPCM7xxClockDividerState *s)
    175{
    176    return divide_by_reg_divisor(s) / 2;
    177}
    178
    179static uint32_t shift_by_reg_divisor(NPCM7xxClockDividerState *s)
    180{
    181    return clock_get_hz(s->clock_in) >>
    182        extract32(s->clk->regs[s->reg], s->offset, s->len);
    183}
    184
    185static NPCM7xxClockPLL find_pll_by_reg(enum NPCM7xxCLKRegisters reg)
    186{
    187    switch (reg) {
    188    case NPCM7XX_CLK_PLLCON0:
    189        return NPCM7XX_CLOCK_PLL0;
    190    case NPCM7XX_CLK_PLLCON1:
    191        return NPCM7XX_CLOCK_PLL1;
    192    case NPCM7XX_CLK_PLLCON2:
    193        return NPCM7XX_CLOCK_PLL2;
    194    case NPCM7XX_CLK_PLLCONG:
    195        return NPCM7XX_CLOCK_PLLG;
    196    default:
    197        g_assert_not_reached();
    198    }
    199}
    200
    201static void npcm7xx_clk_update_all_plls(NPCM7xxCLKState *clk)
    202{
    203    int i;
    204
    205    for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) {
    206        npcm7xx_clk_update_pll(&clk->plls[i]);
    207    }
    208}
    209
    210static void npcm7xx_clk_update_all_sels(NPCM7xxCLKState *clk)
    211{
    212    int i;
    213
    214    for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) {
    215        npcm7xx_clk_update_sel(&clk->sels[i]);
    216    }
    217}
    218
    219static void npcm7xx_clk_update_all_dividers(NPCM7xxCLKState *clk)
    220{
    221    int i;
    222
    223    for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) {
    224        npcm7xx_clk_update_divider(&clk->dividers[i]);
    225    }
    226}
    227
    228static void npcm7xx_clk_update_all_clocks(NPCM7xxCLKState *clk)
    229{
    230    clock_update_hz(clk->clkref, NPCM7XX_CLOCK_REF_HZ);
    231    npcm7xx_clk_update_all_plls(clk);
    232    npcm7xx_clk_update_all_sels(clk);
    233    npcm7xx_clk_update_all_dividers(clk);
    234}
    235
    236/* Types of clock sources. */
    237typedef enum ClockSrcType {
    238    CLKSRC_REF,
    239    CLKSRC_PLL,
    240    CLKSRC_SEL,
    241    CLKSRC_DIV,
    242} ClockSrcType;
    243
    244typedef struct PLLInitInfo {
    245    const char *name;
    246    ClockSrcType src_type;
    247    int src_index;
    248    int reg;
    249    const char *public_name;
    250} PLLInitInfo;
    251
    252typedef struct SELInitInfo {
    253    const char *name;
    254    uint8_t input_size;
    255    ClockSrcType src_type[NPCM7XX_CLK_SEL_MAX_INPUT];
    256    int src_index[NPCM7XX_CLK_SEL_MAX_INPUT];
    257    int offset;
    258    int len;
    259    const char *public_name;
    260} SELInitInfo;
    261
    262typedef struct DividerInitInfo {
    263    const char *name;
    264    ClockSrcType src_type;
    265    int src_index;
    266    uint32_t (*divide)(NPCM7xxClockDividerState *s);
    267    int reg; /* not used when type == CONSTANT */
    268    int offset; /* not used when type == CONSTANT */
    269    int len; /* not used when type == CONSTANT */
    270    int divisor; /* used only when type == CONSTANT */
    271    const char *public_name;
    272} DividerInitInfo;
    273
    274static const PLLInitInfo pll_init_info_list[] = {
    275    [NPCM7XX_CLOCK_PLL0] = {
    276        .name = "pll0",
    277        .src_type = CLKSRC_REF,
    278        .reg = NPCM7XX_CLK_PLLCON0,
    279    },
    280    [NPCM7XX_CLOCK_PLL1] = {
    281        .name = "pll1",
    282        .src_type = CLKSRC_REF,
    283        .reg = NPCM7XX_CLK_PLLCON1,
    284    },
    285    [NPCM7XX_CLOCK_PLL2] = {
    286        .name = "pll2",
    287        .src_type = CLKSRC_REF,
    288        .reg = NPCM7XX_CLK_PLLCON2,
    289    },
    290    [NPCM7XX_CLOCK_PLLG] = {
    291        .name = "pllg",
    292        .src_type = CLKSRC_REF,
    293        .reg = NPCM7XX_CLK_PLLCONG,
    294    },
    295};
    296
    297static const SELInitInfo sel_init_info_list[] = {
    298    [NPCM7XX_CLOCK_PIXCKSEL] = {
    299        .name = "pixcksel",
    300        .input_size = 2,
    301        .src_type = {CLKSRC_PLL, CLKSRC_REF},
    302        .src_index = {NPCM7XX_CLOCK_PLLG, 0},
    303        .offset = 5,
    304        .len = 1,
    305        .public_name = "pixel-clock",
    306    },
    307    [NPCM7XX_CLOCK_MCCKSEL] = {
    308        .name = "mccksel",
    309        .input_size = 4,
    310        .src_type = {CLKSRC_DIV, CLKSRC_REF, CLKSRC_REF,
    311            /*MCBPCK, shouldn't be used in normal operation*/
    312            CLKSRC_REF},
    313        .src_index = {NPCM7XX_CLOCK_PLL1D2, 0, 0, 0},
    314        .offset = 12,
    315        .len = 2,
    316        .public_name = "mc-phy-clock",
    317    },
    318    [NPCM7XX_CLOCK_CPUCKSEL] = {
    319        .name = "cpucksel",
    320        .input_size = 4,
    321        .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF,
    322            /*SYSBPCK, shouldn't be used in normal operation*/
    323            CLKSRC_REF},
    324        .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, 0},
    325        .offset = 0,
    326        .len = 2,
    327        .public_name = "system-clock",
    328    },
    329    [NPCM7XX_CLOCK_CLKOUTSEL] = {
    330        .name = "clkoutsel",
    331        .input_size = 5,
    332        .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF,
    333            CLKSRC_PLL, CLKSRC_DIV},
    334        .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0,
    335            NPCM7XX_CLOCK_PLLG, NPCM7XX_CLOCK_PLL2D2},
    336        .offset = 18,
    337        .len = 3,
    338        .public_name = "tock",
    339    },
    340    [NPCM7XX_CLOCK_UARTCKSEL] = {
    341        .name = "uartcksel",
    342        .input_size = 4,
    343        .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV},
    344        .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0,
    345            NPCM7XX_CLOCK_PLL2D2},
    346        .offset = 8,
    347        .len = 2,
    348    },
    349    [NPCM7XX_CLOCK_TIMCKSEL] = {
    350        .name = "timcksel",
    351        .input_size = 4,
    352        .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV},
    353        .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0,
    354            NPCM7XX_CLOCK_PLL2D2},
    355        .offset = 14,
    356        .len = 2,
    357    },
    358    [NPCM7XX_CLOCK_SDCKSEL] = {
    359        .name = "sdcksel",
    360        .input_size = 4,
    361        .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV},
    362        .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0,
    363            NPCM7XX_CLOCK_PLL2D2},
    364        .offset = 6,
    365        .len = 2,
    366    },
    367    [NPCM7XX_CLOCK_GFXMSEL] = {
    368        .name = "gfxmksel",
    369        .input_size = 2,
    370        .src_type = {CLKSRC_REF, CLKSRC_PLL},
    371        .src_index = {0, NPCM7XX_CLOCK_PLL2},
    372        .offset = 21,
    373        .len = 1,
    374    },
    375    [NPCM7XX_CLOCK_SUCKSEL] = {
    376        .name = "sucksel",
    377        .input_size = 4,
    378        .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV},
    379        .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0,
    380            NPCM7XX_CLOCK_PLL2D2},
    381        .offset = 10,
    382        .len = 2,
    383    },
    384};
    385
    386static const DividerInitInfo divider_init_info_list[] = {
    387    [NPCM7XX_CLOCK_PLL1D2] = {
    388        .name = "pll1d2",
    389        .src_type = CLKSRC_PLL,
    390        .src_index = NPCM7XX_CLOCK_PLL1,
    391        .divide = divide_by_constant,
    392        .divisor = 2,
    393    },
    394    [NPCM7XX_CLOCK_PLL2D2] = {
    395        .name = "pll2d2",
    396        .src_type = CLKSRC_PLL,
    397        .src_index = NPCM7XX_CLOCK_PLL2,
    398        .divide = divide_by_constant,
    399        .divisor = 2,
    400    },
    401    [NPCM7XX_CLOCK_MC_DIVIDER] = {
    402        .name = "mc-divider",
    403        .src_type = CLKSRC_SEL,
    404        .src_index = NPCM7XX_CLOCK_MCCKSEL,
    405        .divide = divide_by_constant,
    406        .divisor = 2,
    407        .public_name = "mc-clock"
    408    },
    409    [NPCM7XX_CLOCK_AXI_DIVIDER] = {
    410        .name = "axi-divider",
    411        .src_type = CLKSRC_SEL,
    412        .src_index = NPCM7XX_CLOCK_CPUCKSEL,
    413        .divide = shift_by_reg_divisor,
    414        .reg = NPCM7XX_CLK_CLKDIV1,
    415        .offset = 0,
    416        .len = 1,
    417        .public_name = "clk2"
    418    },
    419    [NPCM7XX_CLOCK_AHB_DIVIDER] = {
    420        .name = "ahb-divider",
    421        .src_type = CLKSRC_DIV,
    422        .src_index = NPCM7XX_CLOCK_AXI_DIVIDER,
    423        .divide = divide_by_reg_divisor,
    424        .reg = NPCM7XX_CLK_CLKDIV1,
    425        .offset = 26,
    426        .len = 2,
    427        .public_name = "clk4"
    428    },
    429    [NPCM7XX_CLOCK_AHB3_DIVIDER] = {
    430        .name = "ahb3-divider",
    431        .src_type = CLKSRC_DIV,
    432        .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
    433        .divide = divide_by_reg_divisor,
    434        .reg = NPCM7XX_CLK_CLKDIV1,
    435        .offset = 6,
    436        .len = 5,
    437        .public_name = "ahb3-spi3-clock"
    438    },
    439    [NPCM7XX_CLOCK_SPI0_DIVIDER] = {
    440        .name = "spi0-divider",
    441        .src_type = CLKSRC_DIV,
    442        .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
    443        .divide = divide_by_reg_divisor,
    444        .reg = NPCM7XX_CLK_CLKDIV3,
    445        .offset = 6,
    446        .len = 5,
    447        .public_name = "spi0-clock",
    448    },
    449    [NPCM7XX_CLOCK_SPIX_DIVIDER] = {
    450        .name = "spix-divider",
    451        .src_type = CLKSRC_DIV,
    452        .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
    453        .divide = divide_by_reg_divisor,
    454        .reg = NPCM7XX_CLK_CLKDIV3,
    455        .offset = 1,
    456        .len = 5,
    457        .public_name = "spix-clock",
    458    },
    459    [NPCM7XX_CLOCK_APB1_DIVIDER] = {
    460        .name = "apb1-divider",
    461        .src_type = CLKSRC_DIV,
    462        .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
    463        .divide = shift_by_reg_divisor,
    464        .reg = NPCM7XX_CLK_CLKDIV2,
    465        .offset = 24,
    466        .len = 2,
    467        .public_name = "apb1-clock",
    468    },
    469    [NPCM7XX_CLOCK_APB2_DIVIDER] = {
    470        .name = "apb2-divider",
    471        .src_type = CLKSRC_DIV,
    472        .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
    473        .divide = shift_by_reg_divisor,
    474        .reg = NPCM7XX_CLK_CLKDIV2,
    475        .offset = 26,
    476        .len = 2,
    477        .public_name = "apb2-clock",
    478    },
    479    [NPCM7XX_CLOCK_APB3_DIVIDER] = {
    480        .name = "apb3-divider",
    481        .src_type = CLKSRC_DIV,
    482        .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
    483        .divide = shift_by_reg_divisor,
    484        .reg = NPCM7XX_CLK_CLKDIV2,
    485        .offset = 28,
    486        .len = 2,
    487        .public_name = "apb3-clock",
    488    },
    489    [NPCM7XX_CLOCK_APB4_DIVIDER] = {
    490        .name = "apb4-divider",
    491        .src_type = CLKSRC_DIV,
    492        .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
    493        .divide = shift_by_reg_divisor,
    494        .reg = NPCM7XX_CLK_CLKDIV2,
    495        .offset = 30,
    496        .len = 2,
    497        .public_name = "apb4-clock",
    498    },
    499    [NPCM7XX_CLOCK_APB5_DIVIDER] = {
    500        .name = "apb5-divider",
    501        .src_type = CLKSRC_DIV,
    502        .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
    503        .divide = shift_by_reg_divisor,
    504        .reg = NPCM7XX_CLK_CLKDIV2,
    505        .offset = 22,
    506        .len = 2,
    507        .public_name = "apb5-clock",
    508    },
    509    [NPCM7XX_CLOCK_CLKOUT_DIVIDER] = {
    510        .name = "clkout-divider",
    511        .src_type = CLKSRC_SEL,
    512        .src_index = NPCM7XX_CLOCK_CLKOUTSEL,
    513        .divide = divide_by_reg_divisor,
    514        .reg = NPCM7XX_CLK_CLKDIV2,
    515        .offset = 16,
    516        .len = 5,
    517        .public_name = "clkout",
    518    },
    519    [NPCM7XX_CLOCK_UART_DIVIDER] = {
    520        .name = "uart-divider",
    521        .src_type = CLKSRC_SEL,
    522        .src_index = NPCM7XX_CLOCK_UARTCKSEL,
    523        .divide = divide_by_reg_divisor,
    524        .reg = NPCM7XX_CLK_CLKDIV1,
    525        .offset = 16,
    526        .len = 5,
    527        .public_name = "uart-clock",
    528    },
    529    [NPCM7XX_CLOCK_TIMER_DIVIDER] = {
    530        .name = "timer-divider",
    531        .src_type = CLKSRC_SEL,
    532        .src_index = NPCM7XX_CLOCK_TIMCKSEL,
    533        .divide = divide_by_reg_divisor,
    534        .reg = NPCM7XX_CLK_CLKDIV1,
    535        .offset = 21,
    536        .len = 5,
    537        .public_name = "timer-clock",
    538    },
    539    [NPCM7XX_CLOCK_ADC_DIVIDER] = {
    540        .name = "adc-divider",
    541        .src_type = CLKSRC_DIV,
    542        .src_index = NPCM7XX_CLOCK_TIMER_DIVIDER,
    543        .divide = shift_by_reg_divisor,
    544        .reg = NPCM7XX_CLK_CLKDIV1,
    545        .offset = 28,
    546        .len = 3,
    547        .public_name = "adc-clock",
    548    },
    549    [NPCM7XX_CLOCK_MMC_DIVIDER] = {
    550        .name = "mmc-divider",
    551        .src_type = CLKSRC_SEL,
    552        .src_index = NPCM7XX_CLOCK_SDCKSEL,
    553        .divide = divide_by_reg_divisor,
    554        .reg = NPCM7XX_CLK_CLKDIV1,
    555        .offset = 11,
    556        .len = 5,
    557        .public_name = "mmc-clock",
    558    },
    559    [NPCM7XX_CLOCK_SDHC_DIVIDER] = {
    560        .name = "sdhc-divider",
    561        .src_type = CLKSRC_SEL,
    562        .src_index = NPCM7XX_CLOCK_SDCKSEL,
    563        .divide = divide_by_reg_divisor_times_2,
    564        .reg = NPCM7XX_CLK_CLKDIV2,
    565        .offset = 0,
    566        .len = 4,
    567        .public_name = "sdhc-clock",
    568    },
    569    [NPCM7XX_CLOCK_GFXM_DIVIDER] = {
    570        .name = "gfxm-divider",
    571        .src_type = CLKSRC_SEL,
    572        .src_index = NPCM7XX_CLOCK_GFXMSEL,
    573        .divide = divide_by_constant,
    574        .divisor = 3,
    575        .public_name = "gfxm-clock",
    576    },
    577    [NPCM7XX_CLOCK_UTMI_DIVIDER] = {
    578        .name = "utmi-divider",
    579        .src_type = CLKSRC_SEL,
    580        .src_index = NPCM7XX_CLOCK_SUCKSEL,
    581        .divide = divide_by_reg_divisor,
    582        .reg = NPCM7XX_CLK_CLKDIV2,
    583        .offset = 8,
    584        .len = 5,
    585        .public_name = "utmi-clock",
    586    },
    587};
    588
    589static void npcm7xx_clk_update_pll_cb(void *opaque, ClockEvent event)
    590{
    591    npcm7xx_clk_update_pll(opaque);
    592}
    593
    594static void npcm7xx_clk_pll_init(Object *obj)
    595{
    596    NPCM7xxClockPLLState *pll = NPCM7XX_CLOCK_PLL(obj);
    597
    598    pll->clock_in = qdev_init_clock_in(DEVICE(pll), "clock-in",
    599                                       npcm7xx_clk_update_pll_cb, pll,
    600                                       ClockUpdate);
    601    pll->clock_out = qdev_init_clock_out(DEVICE(pll), "clock-out");
    602}
    603
    604static void npcm7xx_clk_update_sel_cb(void *opaque, ClockEvent event)
    605{
    606    npcm7xx_clk_update_sel(opaque);
    607}
    608
    609static void npcm7xx_clk_sel_init(Object *obj)
    610{
    611    int i;
    612    NPCM7xxClockSELState *sel = NPCM7XX_CLOCK_SEL(obj);
    613
    614    for (i = 0; i < NPCM7XX_CLK_SEL_MAX_INPUT; ++i) {
    615        sel->clock_in[i] = qdev_init_clock_in(DEVICE(sel),
    616                g_strdup_printf("clock-in[%d]", i),
    617                npcm7xx_clk_update_sel_cb, sel, ClockUpdate);
    618    }
    619    sel->clock_out = qdev_init_clock_out(DEVICE(sel), "clock-out");
    620}
    621
    622static void npcm7xx_clk_update_divider_cb(void *opaque, ClockEvent event)
    623{
    624    npcm7xx_clk_update_divider(opaque);
    625}
    626
    627static void npcm7xx_clk_divider_init(Object *obj)
    628{
    629    NPCM7xxClockDividerState *div = NPCM7XX_CLOCK_DIVIDER(obj);
    630
    631    div->clock_in = qdev_init_clock_in(DEVICE(div), "clock-in",
    632                                       npcm7xx_clk_update_divider_cb,
    633                                       div, ClockUpdate);
    634    div->clock_out = qdev_init_clock_out(DEVICE(div), "clock-out");
    635}
    636
    637static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState *pll,
    638        NPCM7xxCLKState *clk, const PLLInitInfo *init_info)
    639{
    640    pll->name = init_info->name;
    641    pll->clk = clk;
    642    pll->reg = init_info->reg;
    643    if (init_info->public_name != NULL) {
    644        qdev_alias_clock(DEVICE(pll), "clock-out", DEVICE(clk),
    645                init_info->public_name);
    646    }
    647}
    648
    649static void npcm7xx_init_clock_sel(NPCM7xxClockSELState *sel,
    650        NPCM7xxCLKState *clk, const SELInitInfo *init_info)
    651{
    652    int input_size = init_info->input_size;
    653
    654    sel->name = init_info->name;
    655    sel->clk = clk;
    656    sel->input_size = init_info->input_size;
    657    g_assert(input_size <= NPCM7XX_CLK_SEL_MAX_INPUT);
    658    sel->offset = init_info->offset;
    659    sel->len = init_info->len;
    660    if (init_info->public_name != NULL) {
    661        qdev_alias_clock(DEVICE(sel), "clock-out", DEVICE(clk),
    662                init_info->public_name);
    663    }
    664}
    665
    666static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState *div,
    667        NPCM7xxCLKState *clk, const DividerInitInfo *init_info)
    668{
    669    div->name = init_info->name;
    670    div->clk = clk;
    671
    672    div->divide = init_info->divide;
    673    if (div->divide == divide_by_constant) {
    674        div->divisor = init_info->divisor;
    675    } else {
    676        div->reg = init_info->reg;
    677        div->offset = init_info->offset;
    678        div->len = init_info->len;
    679    }
    680    if (init_info->public_name != NULL) {
    681        qdev_alias_clock(DEVICE(div), "clock-out", DEVICE(clk),
    682                init_info->public_name);
    683    }
    684}
    685
    686static Clock *npcm7xx_get_clock(NPCM7xxCLKState *clk, ClockSrcType type,
    687        int index)
    688{
    689    switch (type) {
    690    case CLKSRC_REF:
    691        return clk->clkref;
    692    case CLKSRC_PLL:
    693        return clk->plls[index].clock_out;
    694    case CLKSRC_SEL:
    695        return clk->sels[index].clock_out;
    696    case CLKSRC_DIV:
    697        return clk->dividers[index].clock_out;
    698    default:
    699        g_assert_not_reached();
    700    }
    701}
    702
    703static void npcm7xx_connect_clocks(NPCM7xxCLKState *clk)
    704{
    705    int i, j;
    706    Clock *src;
    707
    708    for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) {
    709        src = npcm7xx_get_clock(clk, pll_init_info_list[i].src_type,
    710                pll_init_info_list[i].src_index);
    711        clock_set_source(clk->plls[i].clock_in, src);
    712    }
    713    for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) {
    714        for (j = 0; j < sel_init_info_list[i].input_size; ++j) {
    715            src = npcm7xx_get_clock(clk, sel_init_info_list[i].src_type[j],
    716                    sel_init_info_list[i].src_index[j]);
    717            clock_set_source(clk->sels[i].clock_in[j], src);
    718        }
    719    }
    720    for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) {
    721        src = npcm7xx_get_clock(clk, divider_init_info_list[i].src_type,
    722                divider_init_info_list[i].src_index);
    723        clock_set_source(clk->dividers[i].clock_in, src);
    724    }
    725}
    726
    727static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size)
    728{
    729    uint32_t reg = offset / sizeof(uint32_t);
    730    NPCM7xxCLKState *s = opaque;
    731    int64_t now_ns;
    732    uint32_t value = 0;
    733
    734    if (reg >= NPCM7XX_CLK_NR_REGS) {
    735        qemu_log_mask(LOG_GUEST_ERROR,
    736                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
    737                      __func__, offset);
    738        return 0;
    739    }
    740
    741    switch (reg) {
    742    case NPCM7XX_CLK_SWRSTR:
    743        qemu_log_mask(LOG_GUEST_ERROR,
    744                      "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n",
    745                      __func__, offset);
    746        break;
    747
    748    case NPCM7XX_CLK_SECCNT:
    749        now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    750        value = (now_ns - s->ref_ns) / NANOSECONDS_PER_SECOND;
    751        break;
    752
    753    case NPCM7XX_CLK_CNTR25M:
    754        now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    755        /*
    756         * This register counts 25 MHz cycles, updating every 640 ns. It rolls
    757         * over to zero every second.
    758         *
    759         * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000.
    760         */
    761        value = (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_CLOCK_REF_HZ;
    762        break;
    763
    764    default:
    765        value = s->regs[reg];
    766        break;
    767    };
    768
    769    trace_npcm7xx_clk_read(offset, value);
    770
    771    return value;
    772}
    773
    774static void npcm7xx_clk_write(void *opaque, hwaddr offset,
    775                              uint64_t v, unsigned size)
    776{
    777    uint32_t reg = offset / sizeof(uint32_t);
    778    NPCM7xxCLKState *s = opaque;
    779    uint32_t value = v;
    780
    781    trace_npcm7xx_clk_write(offset, value);
    782
    783    if (reg >= NPCM7XX_CLK_NR_REGS) {
    784        qemu_log_mask(LOG_GUEST_ERROR,
    785                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
    786                      __func__, offset);
    787        return;
    788    }
    789
    790    switch (reg) {
    791    case NPCM7XX_CLK_SWRSTR:
    792        qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n",
    793                      __func__, value);
    794        value = 0;
    795        break;
    796
    797    case NPCM7XX_CLK_PLLCON0:
    798    case NPCM7XX_CLK_PLLCON1:
    799    case NPCM7XX_CLK_PLLCON2:
    800    case NPCM7XX_CLK_PLLCONG:
    801        if (value & PLLCON_PWDEN) {
    802            /* Power down -- clear lock and indicate loss of lock */
    803            value &= ~PLLCON_LOKI;
    804            value |= PLLCON_LOKS;
    805        } else {
    806            /* Normal mode -- assume always locked */
    807            value |= PLLCON_LOKI;
    808            /* Keep LOKS unchanged unless cleared by writing 1 */
    809            if (value & PLLCON_LOKS) {
    810                value &= ~PLLCON_LOKS;
    811            } else {
    812                value |= (value & PLLCON_LOKS);
    813            }
    814        }
    815        /* Only update PLL when it is locked. */
    816        if (value & PLLCON_LOKI) {
    817            npcm7xx_clk_update_pll(&s->plls[find_pll_by_reg(reg)]);
    818        }
    819        break;
    820
    821    case NPCM7XX_CLK_CLKSEL:
    822        npcm7xx_clk_update_all_sels(s);
    823        break;
    824
    825    case NPCM7XX_CLK_CLKDIV1:
    826    case NPCM7XX_CLK_CLKDIV2:
    827    case NPCM7XX_CLK_CLKDIV3:
    828        npcm7xx_clk_update_all_dividers(s);
    829        break;
    830
    831    case NPCM7XX_CLK_CNTR25M:
    832        qemu_log_mask(LOG_GUEST_ERROR,
    833                      "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
    834                      __func__, offset);
    835        return;
    836    }
    837
    838    s->regs[reg] = value;
    839}
    840
    841/* Perform reset action triggered by a watchdog */
    842static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n,
    843        int level)
    844{
    845    NPCM7xxCLKState *clk = NPCM7XX_CLK(opaque);
    846    uint32_t rcr;
    847
    848    g_assert(n >= 0 && n <= NPCM7XX_NR_WATCHDOGS);
    849    rcr = clk->regs[NPCM7XX_CLK_WD0RCR + n];
    850    if (rcr & NPCM7XX_CLK_WDRCR_CA9C) {
    851        watchdog_perform_action();
    852    } else {
    853        qemu_log_mask(LOG_UNIMP,
    854                "%s: only CPU reset is implemented. (requested 0x%" PRIx32")\n",
    855                __func__, rcr);
    856    }
    857}
    858
    859static const struct MemoryRegionOps npcm7xx_clk_ops = {
    860    .read       = npcm7xx_clk_read,
    861    .write      = npcm7xx_clk_write,
    862    .endianness = DEVICE_LITTLE_ENDIAN,
    863    .valid      = {
    864        .min_access_size        = 4,
    865        .max_access_size        = 4,
    866        .unaligned              = false,
    867    },
    868};
    869
    870static void npcm7xx_clk_enter_reset(Object *obj, ResetType type)
    871{
    872    NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
    873
    874    QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
    875
    876    switch (type) {
    877    case RESET_TYPE_COLD:
    878        memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values));
    879        s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    880        npcm7xx_clk_update_all_clocks(s);
    881        return;
    882    }
    883
    884    /*
    885     * A small number of registers need to be reset on a core domain reset,
    886     * but no such reset type exists yet.
    887     */
    888    qemu_log_mask(LOG_UNIMP, "%s: reset type %d not implemented.",
    889                  __func__, type);
    890}
    891
    892static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s)
    893{
    894    int i;
    895
    896    s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL, 0);
    897
    898    /* First pass: init all converter modules */
    899    QEMU_BUILD_BUG_ON(ARRAY_SIZE(pll_init_info_list) != NPCM7XX_CLOCK_NR_PLLS);
    900    QEMU_BUILD_BUG_ON(ARRAY_SIZE(sel_init_info_list) != NPCM7XX_CLOCK_NR_SELS);
    901    QEMU_BUILD_BUG_ON(ARRAY_SIZE(divider_init_info_list)
    902            != NPCM7XX_CLOCK_NR_DIVIDERS);
    903    for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) {
    904        object_initialize_child(OBJECT(s), pll_init_info_list[i].name,
    905                &s->plls[i], TYPE_NPCM7XX_CLOCK_PLL);
    906        npcm7xx_init_clock_pll(&s->plls[i], s,
    907                &pll_init_info_list[i]);
    908    }
    909    for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) {
    910        object_initialize_child(OBJECT(s), sel_init_info_list[i].name,
    911                &s->sels[i], TYPE_NPCM7XX_CLOCK_SEL);
    912        npcm7xx_init_clock_sel(&s->sels[i], s,
    913                &sel_init_info_list[i]);
    914    }
    915    for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) {
    916        object_initialize_child(OBJECT(s), divider_init_info_list[i].name,
    917                &s->dividers[i], TYPE_NPCM7XX_CLOCK_DIVIDER);
    918        npcm7xx_init_clock_divider(&s->dividers[i], s,
    919                &divider_init_info_list[i]);
    920    }
    921
    922    /* Second pass: connect converter modules */
    923    npcm7xx_connect_clocks(s);
    924
    925    clock_update_hz(s->clkref, NPCM7XX_CLOCK_REF_HZ);
    926}
    927
    928static void npcm7xx_clk_init(Object *obj)
    929{
    930    NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
    931
    932    memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s,
    933                          TYPE_NPCM7XX_CLK, 4 * KiB);
    934    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
    935}
    936
    937static int npcm7xx_clk_post_load(void *opaque, int version_id)
    938{
    939    if (version_id >= 1) {
    940        NPCM7xxCLKState *clk = opaque;
    941
    942        npcm7xx_clk_update_all_clocks(clk);
    943    }
    944
    945    return 0;
    946}
    947
    948static void npcm7xx_clk_realize(DeviceState *dev, Error **errp)
    949{
    950    int i;
    951    NPCM7xxCLKState *s = NPCM7XX_CLK(dev);
    952
    953    qdev_init_gpio_in_named(DEVICE(s), npcm7xx_clk_perform_watchdog_reset,
    954            NPCM7XX_WATCHDOG_RESET_GPIO_IN, NPCM7XX_NR_WATCHDOGS);
    955    npcm7xx_clk_init_clock_hierarchy(s);
    956
    957    /* Realize child devices */
    958    for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) {
    959        if (!qdev_realize(DEVICE(&s->plls[i]), NULL, errp)) {
    960            return;
    961        }
    962    }
    963    for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) {
    964        if (!qdev_realize(DEVICE(&s->sels[i]), NULL, errp)) {
    965            return;
    966        }
    967    }
    968    for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) {
    969        if (!qdev_realize(DEVICE(&s->dividers[i]), NULL, errp)) {
    970            return;
    971        }
    972    }
    973}
    974
    975static const VMStateDescription vmstate_npcm7xx_clk_pll = {
    976    .name = "npcm7xx-clock-pll",
    977    .version_id = 0,
    978    .minimum_version_id = 0,
    979    .fields =  (VMStateField[]) {
    980        VMSTATE_CLOCK(clock_in, NPCM7xxClockPLLState),
    981        VMSTATE_END_OF_LIST(),
    982    },
    983};
    984
    985static const VMStateDescription vmstate_npcm7xx_clk_sel = {
    986    .name = "npcm7xx-clock-sel",
    987    .version_id = 0,
    988    .minimum_version_id = 0,
    989    .fields =  (VMStateField[]) {
    990        VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(clock_in, NPCM7xxClockSELState,
    991                NPCM7XX_CLK_SEL_MAX_INPUT, 0, vmstate_clock, Clock),
    992        VMSTATE_END_OF_LIST(),
    993    },
    994};
    995
    996static const VMStateDescription vmstate_npcm7xx_clk_divider = {
    997    .name = "npcm7xx-clock-divider",
    998    .version_id = 0,
    999    .minimum_version_id = 0,
   1000    .fields =  (VMStateField[]) {
   1001        VMSTATE_CLOCK(clock_in, NPCM7xxClockDividerState),
   1002        VMSTATE_END_OF_LIST(),
   1003    },
   1004};
   1005
   1006static const VMStateDescription vmstate_npcm7xx_clk = {
   1007    .name = "npcm7xx-clk",
   1008    .version_id = 1,
   1009    .minimum_version_id = 1,
   1010    .post_load = npcm7xx_clk_post_load,
   1011    .fields = (VMStateField[]) {
   1012        VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS),
   1013        VMSTATE_INT64(ref_ns, NPCM7xxCLKState),
   1014        VMSTATE_CLOCK(clkref, NPCM7xxCLKState),
   1015        VMSTATE_END_OF_LIST(),
   1016    },
   1017};
   1018
   1019static void npcm7xx_clk_pll_class_init(ObjectClass *klass, void *data)
   1020{
   1021    DeviceClass *dc = DEVICE_CLASS(klass);
   1022
   1023    dc->desc = "NPCM7xx Clock PLL Module";
   1024    dc->vmsd = &vmstate_npcm7xx_clk_pll;
   1025}
   1026
   1027static void npcm7xx_clk_sel_class_init(ObjectClass *klass, void *data)
   1028{
   1029    DeviceClass *dc = DEVICE_CLASS(klass);
   1030
   1031    dc->desc = "NPCM7xx Clock SEL Module";
   1032    dc->vmsd = &vmstate_npcm7xx_clk_sel;
   1033}
   1034
   1035static void npcm7xx_clk_divider_class_init(ObjectClass *klass, void *data)
   1036{
   1037    DeviceClass *dc = DEVICE_CLASS(klass);
   1038
   1039    dc->desc = "NPCM7xx Clock Divider Module";
   1040    dc->vmsd = &vmstate_npcm7xx_clk_divider;
   1041}
   1042
   1043static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
   1044{
   1045    ResettableClass *rc = RESETTABLE_CLASS(klass);
   1046    DeviceClass *dc = DEVICE_CLASS(klass);
   1047
   1048    QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM7XX_CLK_NR_REGS);
   1049
   1050    dc->desc = "NPCM7xx Clock Control Registers";
   1051    dc->vmsd = &vmstate_npcm7xx_clk;
   1052    dc->realize = npcm7xx_clk_realize;
   1053    rc->phases.enter = npcm7xx_clk_enter_reset;
   1054}
   1055
   1056static const TypeInfo npcm7xx_clk_pll_info = {
   1057    .name               = TYPE_NPCM7XX_CLOCK_PLL,
   1058    .parent             = TYPE_DEVICE,
   1059    .instance_size      = sizeof(NPCM7xxClockPLLState),
   1060    .instance_init      = npcm7xx_clk_pll_init,
   1061    .class_init         = npcm7xx_clk_pll_class_init,
   1062};
   1063
   1064static const TypeInfo npcm7xx_clk_sel_info = {
   1065    .name               = TYPE_NPCM7XX_CLOCK_SEL,
   1066    .parent             = TYPE_DEVICE,
   1067    .instance_size      = sizeof(NPCM7xxClockSELState),
   1068    .instance_init      = npcm7xx_clk_sel_init,
   1069    .class_init         = npcm7xx_clk_sel_class_init,
   1070};
   1071
   1072static const TypeInfo npcm7xx_clk_divider_info = {
   1073    .name               = TYPE_NPCM7XX_CLOCK_DIVIDER,
   1074    .parent             = TYPE_DEVICE,
   1075    .instance_size      = sizeof(NPCM7xxClockDividerState),
   1076    .instance_init      = npcm7xx_clk_divider_init,
   1077    .class_init         = npcm7xx_clk_divider_class_init,
   1078};
   1079
   1080static const TypeInfo npcm7xx_clk_info = {
   1081    .name               = TYPE_NPCM7XX_CLK,
   1082    .parent             = TYPE_SYS_BUS_DEVICE,
   1083    .instance_size      = sizeof(NPCM7xxCLKState),
   1084    .instance_init      = npcm7xx_clk_init,
   1085    .class_init         = npcm7xx_clk_class_init,
   1086};
   1087
   1088static void npcm7xx_clk_register_type(void)
   1089{
   1090    type_register_static(&npcm7xx_clk_pll_info);
   1091    type_register_static(&npcm7xx_clk_sel_info);
   1092    type_register_static(&npcm7xx_clk_divider_info);
   1093    type_register_static(&npcm7xx_clk_info);
   1094}
   1095type_init(npcm7xx_clk_register_type);