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