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