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 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 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 */ 84 static 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 121 static 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 139 static 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 154 static 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 163 static uint32_t divide_by_constant(NPCM7xxClockDividerState *s) 164 { 165 return clock_get_hz(s->clock_in) / s->divisor; 166 } 167 168 static 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 174 static uint32_t divide_by_reg_divisor_times_2(NPCM7xxClockDividerState *s) 175 { 176 return divide_by_reg_divisor(s) / 2; 177 } 178 179 static 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 185 static 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 201 static 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 210 static 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 219 static 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 228 static 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. */ 237 typedef enum ClockSrcType { 238 CLKSRC_REF, 239 CLKSRC_PLL, 240 CLKSRC_SEL, 241 CLKSRC_DIV, 242 } ClockSrcType; 243 244 typedef 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 252 typedef 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 262 typedef 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 274 static 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 297 static 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 386 static 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 589 static void npcm7xx_clk_pll_init(Object *obj) 590 { 591 NPCM7xxClockPLLState *pll = NPCM7XX_CLOCK_PLL(obj); 592 593 pll->clock_in = qdev_init_clock_in(DEVICE(pll), "clock-in", 594 npcm7xx_clk_update_pll, pll); 595 pll->clock_out = qdev_init_clock_out(DEVICE(pll), "clock-out"); 596 } 597 598 static void npcm7xx_clk_sel_init(Object *obj) 599 { 600 int i; 601 NPCM7xxClockSELState *sel = NPCM7XX_CLOCK_SEL(obj); 602 603 for (i = 0; i < NPCM7XX_CLK_SEL_MAX_INPUT; ++i) { 604 sel->clock_in[i] = qdev_init_clock_in(DEVICE(sel), 605 g_strdup_printf("clock-in[%d]", i), 606 npcm7xx_clk_update_sel, sel); 607 } 608 sel->clock_out = qdev_init_clock_out(DEVICE(sel), "clock-out"); 609 } 610 static void npcm7xx_clk_divider_init(Object *obj) 611 { 612 NPCM7xxClockDividerState *div = NPCM7XX_CLOCK_DIVIDER(obj); 613 614 div->clock_in = qdev_init_clock_in(DEVICE(div), "clock-in", 615 npcm7xx_clk_update_divider, div); 616 div->clock_out = qdev_init_clock_out(DEVICE(div), "clock-out"); 617 } 618 619 static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState *pll, 620 NPCM7xxCLKState *clk, const PLLInitInfo *init_info) 621 { 622 pll->name = init_info->name; 623 pll->clk = clk; 624 pll->reg = init_info->reg; 625 if (init_info->public_name != NULL) { 626 qdev_alias_clock(DEVICE(pll), "clock-out", DEVICE(clk), 627 init_info->public_name); 628 } 629 } 630 631 static void npcm7xx_init_clock_sel(NPCM7xxClockSELState *sel, 632 NPCM7xxCLKState *clk, const SELInitInfo *init_info) 633 { 634 int input_size = init_info->input_size; 635 636 sel->name = init_info->name; 637 sel->clk = clk; 638 sel->input_size = init_info->input_size; 639 g_assert(input_size <= NPCM7XX_CLK_SEL_MAX_INPUT); 640 sel->offset = init_info->offset; 641 sel->len = init_info->len; 642 if (init_info->public_name != NULL) { 643 qdev_alias_clock(DEVICE(sel), "clock-out", DEVICE(clk), 644 init_info->public_name); 645 } 646 } 647 648 static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState *div, 649 NPCM7xxCLKState *clk, const DividerInitInfo *init_info) 650 { 651 div->name = init_info->name; 652 div->clk = clk; 653 654 div->divide = init_info->divide; 655 if (div->divide == divide_by_constant) { 656 div->divisor = init_info->divisor; 657 } else { 658 div->reg = init_info->reg; 659 div->offset = init_info->offset; 660 div->len = init_info->len; 661 } 662 if (init_info->public_name != NULL) { 663 qdev_alias_clock(DEVICE(div), "clock-out", DEVICE(clk), 664 init_info->public_name); 665 } 666 } 667 668 static Clock *npcm7xx_get_clock(NPCM7xxCLKState *clk, ClockSrcType type, 669 int index) 670 { 671 switch (type) { 672 case CLKSRC_REF: 673 return clk->clkref; 674 case CLKSRC_PLL: 675 return clk->plls[index].clock_out; 676 case CLKSRC_SEL: 677 return clk->sels[index].clock_out; 678 case CLKSRC_DIV: 679 return clk->dividers[index].clock_out; 680 default: 681 g_assert_not_reached(); 682 } 683 } 684 685 static void npcm7xx_connect_clocks(NPCM7xxCLKState *clk) 686 { 687 int i, j; 688 Clock *src; 689 690 for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { 691 src = npcm7xx_get_clock(clk, pll_init_info_list[i].src_type, 692 pll_init_info_list[i].src_index); 693 clock_set_source(clk->plls[i].clock_in, src); 694 } 695 for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { 696 for (j = 0; j < sel_init_info_list[i].input_size; ++j) { 697 src = npcm7xx_get_clock(clk, sel_init_info_list[i].src_type[j], 698 sel_init_info_list[i].src_index[j]); 699 clock_set_source(clk->sels[i].clock_in[j], src); 700 } 701 } 702 for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { 703 src = npcm7xx_get_clock(clk, divider_init_info_list[i].src_type, 704 divider_init_info_list[i].src_index); 705 clock_set_source(clk->dividers[i].clock_in, src); 706 } 707 } 708 709 static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size) 710 { 711 uint32_t reg = offset / sizeof(uint32_t); 712 NPCM7xxCLKState *s = opaque; 713 int64_t now_ns; 714 uint32_t value = 0; 715 716 if (reg >= NPCM7XX_CLK_NR_REGS) { 717 qemu_log_mask(LOG_GUEST_ERROR, 718 "%s: offset 0x%04" HWADDR_PRIx " out of range\n", 719 __func__, offset); 720 return 0; 721 } 722 723 switch (reg) { 724 case NPCM7XX_CLK_SWRSTR: 725 qemu_log_mask(LOG_GUEST_ERROR, 726 "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n", 727 __func__, offset); 728 break; 729 730 case NPCM7XX_CLK_SECCNT: 731 now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 732 value = (now_ns - s->ref_ns) / NANOSECONDS_PER_SECOND; 733 break; 734 735 case NPCM7XX_CLK_CNTR25M: 736 now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 737 /* 738 * This register counts 25 MHz cycles, updating every 640 ns. It rolls 739 * over to zero every second. 740 * 741 * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000. 742 */ 743 value = (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_CLOCK_REF_HZ; 744 break; 745 746 default: 747 value = s->regs[reg]; 748 break; 749 }; 750 751 trace_npcm7xx_clk_read(offset, value); 752 753 return value; 754 } 755 756 static void npcm7xx_clk_write(void *opaque, hwaddr offset, 757 uint64_t v, unsigned size) 758 { 759 uint32_t reg = offset / sizeof(uint32_t); 760 NPCM7xxCLKState *s = opaque; 761 uint32_t value = v; 762 763 trace_npcm7xx_clk_write(offset, value); 764 765 if (reg >= NPCM7XX_CLK_NR_REGS) { 766 qemu_log_mask(LOG_GUEST_ERROR, 767 "%s: offset 0x%04" HWADDR_PRIx " out of range\n", 768 __func__, offset); 769 return; 770 } 771 772 switch (reg) { 773 case NPCM7XX_CLK_SWRSTR: 774 qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n", 775 __func__, value); 776 value = 0; 777 break; 778 779 case NPCM7XX_CLK_PLLCON0: 780 case NPCM7XX_CLK_PLLCON1: 781 case NPCM7XX_CLK_PLLCON2: 782 case NPCM7XX_CLK_PLLCONG: 783 if (value & PLLCON_PWDEN) { 784 /* Power down -- clear lock and indicate loss of lock */ 785 value &= ~PLLCON_LOKI; 786 value |= PLLCON_LOKS; 787 } else { 788 /* Normal mode -- assume always locked */ 789 value |= PLLCON_LOKI; 790 /* Keep LOKS unchanged unless cleared by writing 1 */ 791 if (value & PLLCON_LOKS) { 792 value &= ~PLLCON_LOKS; 793 } else { 794 value |= (value & PLLCON_LOKS); 795 } 796 } 797 /* Only update PLL when it is locked. */ 798 if (value & PLLCON_LOKI) { 799 npcm7xx_clk_update_pll(&s->plls[find_pll_by_reg(reg)]); 800 } 801 break; 802 803 case NPCM7XX_CLK_CLKSEL: 804 npcm7xx_clk_update_all_sels(s); 805 break; 806 807 case NPCM7XX_CLK_CLKDIV1: 808 case NPCM7XX_CLK_CLKDIV2: 809 case NPCM7XX_CLK_CLKDIV3: 810 npcm7xx_clk_update_all_dividers(s); 811 break; 812 813 case NPCM7XX_CLK_CNTR25M: 814 qemu_log_mask(LOG_GUEST_ERROR, 815 "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", 816 __func__, offset); 817 return; 818 } 819 820 s->regs[reg] = value; 821 } 822 823 /* Perform reset action triggered by a watchdog */ 824 static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n, 825 int level) 826 { 827 NPCM7xxCLKState *clk = NPCM7XX_CLK(opaque); 828 uint32_t rcr; 829 830 g_assert(n >= 0 && n <= NPCM7XX_NR_WATCHDOGS); 831 rcr = clk->regs[NPCM7XX_CLK_WD0RCR + n]; 832 if (rcr & NPCM7XX_CLK_WDRCR_CA9C) { 833 watchdog_perform_action(); 834 } else { 835 qemu_log_mask(LOG_UNIMP, 836 "%s: only CPU reset is implemented. (requested 0x%" PRIx32")\n", 837 __func__, rcr); 838 } 839 } 840 841 static const struct MemoryRegionOps npcm7xx_clk_ops = { 842 .read = npcm7xx_clk_read, 843 .write = npcm7xx_clk_write, 844 .endianness = DEVICE_LITTLE_ENDIAN, 845 .valid = { 846 .min_access_size = 4, 847 .max_access_size = 4, 848 .unaligned = false, 849 }, 850 }; 851 852 static void npcm7xx_clk_enter_reset(Object *obj, ResetType type) 853 { 854 NPCM7xxCLKState *s = NPCM7XX_CLK(obj); 855 856 QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values)); 857 858 switch (type) { 859 case RESET_TYPE_COLD: 860 memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values)); 861 s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 862 npcm7xx_clk_update_all_clocks(s); 863 return; 864 } 865 866 /* 867 * A small number of registers need to be reset on a core domain reset, 868 * but no such reset type exists yet. 869 */ 870 qemu_log_mask(LOG_UNIMP, "%s: reset type %d not implemented.", 871 __func__, type); 872 } 873 874 static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s) 875 { 876 int i; 877 878 s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL); 879 880 /* First pass: init all converter modules */ 881 QEMU_BUILD_BUG_ON(ARRAY_SIZE(pll_init_info_list) != NPCM7XX_CLOCK_NR_PLLS); 882 QEMU_BUILD_BUG_ON(ARRAY_SIZE(sel_init_info_list) != NPCM7XX_CLOCK_NR_SELS); 883 QEMU_BUILD_BUG_ON(ARRAY_SIZE(divider_init_info_list) 884 != NPCM7XX_CLOCK_NR_DIVIDERS); 885 for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { 886 object_initialize_child(OBJECT(s), pll_init_info_list[i].name, 887 &s->plls[i], TYPE_NPCM7XX_CLOCK_PLL); 888 npcm7xx_init_clock_pll(&s->plls[i], s, 889 &pll_init_info_list[i]); 890 } 891 for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { 892 object_initialize_child(OBJECT(s), sel_init_info_list[i].name, 893 &s->sels[i], TYPE_NPCM7XX_CLOCK_SEL); 894 npcm7xx_init_clock_sel(&s->sels[i], s, 895 &sel_init_info_list[i]); 896 } 897 for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { 898 object_initialize_child(OBJECT(s), divider_init_info_list[i].name, 899 &s->dividers[i], TYPE_NPCM7XX_CLOCK_DIVIDER); 900 npcm7xx_init_clock_divider(&s->dividers[i], s, 901 ÷r_init_info_list[i]); 902 } 903 904 /* Second pass: connect converter modules */ 905 npcm7xx_connect_clocks(s); 906 907 clock_update_hz(s->clkref, NPCM7XX_CLOCK_REF_HZ); 908 } 909 910 static void npcm7xx_clk_init(Object *obj) 911 { 912 NPCM7xxCLKState *s = NPCM7XX_CLK(obj); 913 914 memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s, 915 TYPE_NPCM7XX_CLK, 4 * KiB); 916 sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); 917 } 918 919 static int npcm7xx_clk_post_load(void *opaque, int version_id) 920 { 921 if (version_id >= 1) { 922 NPCM7xxCLKState *clk = opaque; 923 924 npcm7xx_clk_update_all_clocks(clk); 925 } 926 927 return 0; 928 } 929 930 static void npcm7xx_clk_realize(DeviceState *dev, Error **errp) 931 { 932 int i; 933 NPCM7xxCLKState *s = NPCM7XX_CLK(dev); 934 935 qdev_init_gpio_in_named(DEVICE(s), npcm7xx_clk_perform_watchdog_reset, 936 NPCM7XX_WATCHDOG_RESET_GPIO_IN, NPCM7XX_NR_WATCHDOGS); 937 npcm7xx_clk_init_clock_hierarchy(s); 938 939 /* Realize child devices */ 940 for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { 941 if (!qdev_realize(DEVICE(&s->plls[i]), NULL, errp)) { 942 return; 943 } 944 } 945 for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { 946 if (!qdev_realize(DEVICE(&s->sels[i]), NULL, errp)) { 947 return; 948 } 949 } 950 for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { 951 if (!qdev_realize(DEVICE(&s->dividers[i]), NULL, errp)) { 952 return; 953 } 954 } 955 } 956 957 static const VMStateDescription vmstate_npcm7xx_clk_pll = { 958 .name = "npcm7xx-clock-pll", 959 .version_id = 0, 960 .minimum_version_id = 0, 961 .fields = (VMStateField[]) { 962 VMSTATE_CLOCK(clock_in, NPCM7xxClockPLLState), 963 VMSTATE_END_OF_LIST(), 964 }, 965 }; 966 967 static const VMStateDescription vmstate_npcm7xx_clk_sel = { 968 .name = "npcm7xx-clock-sel", 969 .version_id = 0, 970 .minimum_version_id = 0, 971 .fields = (VMStateField[]) { 972 VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(clock_in, NPCM7xxClockSELState, 973 NPCM7XX_CLK_SEL_MAX_INPUT, 0, vmstate_clock, Clock), 974 VMSTATE_END_OF_LIST(), 975 }, 976 }; 977 978 static const VMStateDescription vmstate_npcm7xx_clk_divider = { 979 .name = "npcm7xx-clock-divider", 980 .version_id = 0, 981 .minimum_version_id = 0, 982 .fields = (VMStateField[]) { 983 VMSTATE_CLOCK(clock_in, NPCM7xxClockDividerState), 984 VMSTATE_END_OF_LIST(), 985 }, 986 }; 987 988 static const VMStateDescription vmstate_npcm7xx_clk = { 989 .name = "npcm7xx-clk", 990 .version_id = 1, 991 .minimum_version_id = 1, 992 .post_load = npcm7xx_clk_post_load, 993 .fields = (VMStateField[]) { 994 VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS), 995 VMSTATE_INT64(ref_ns, NPCM7xxCLKState), 996 VMSTATE_CLOCK(clkref, NPCM7xxCLKState), 997 VMSTATE_END_OF_LIST(), 998 }, 999 }; 1000 1001 static void npcm7xx_clk_pll_class_init(ObjectClass *klass, void *data) 1002 { 1003 DeviceClass *dc = DEVICE_CLASS(klass); 1004 1005 dc->desc = "NPCM7xx Clock PLL Module"; 1006 dc->vmsd = &vmstate_npcm7xx_clk_pll; 1007 } 1008 1009 static void npcm7xx_clk_sel_class_init(ObjectClass *klass, void *data) 1010 { 1011 DeviceClass *dc = DEVICE_CLASS(klass); 1012 1013 dc->desc = "NPCM7xx Clock SEL Module"; 1014 dc->vmsd = &vmstate_npcm7xx_clk_sel; 1015 } 1016 1017 static void npcm7xx_clk_divider_class_init(ObjectClass *klass, void *data) 1018 { 1019 DeviceClass *dc = DEVICE_CLASS(klass); 1020 1021 dc->desc = "NPCM7xx Clock Divider Module"; 1022 dc->vmsd = &vmstate_npcm7xx_clk_divider; 1023 } 1024 1025 static void npcm7xx_clk_class_init(ObjectClass *klass, void *data) 1026 { 1027 ResettableClass *rc = RESETTABLE_CLASS(klass); 1028 DeviceClass *dc = DEVICE_CLASS(klass); 1029 1030 QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM7XX_CLK_NR_REGS); 1031 1032 dc->desc = "NPCM7xx Clock Control Registers"; 1033 dc->vmsd = &vmstate_npcm7xx_clk; 1034 dc->realize = npcm7xx_clk_realize; 1035 rc->phases.enter = npcm7xx_clk_enter_reset; 1036 } 1037 1038 static const TypeInfo npcm7xx_clk_pll_info = { 1039 .name = TYPE_NPCM7XX_CLOCK_PLL, 1040 .parent = TYPE_DEVICE, 1041 .instance_size = sizeof(NPCM7xxClockPLLState), 1042 .instance_init = npcm7xx_clk_pll_init, 1043 .class_init = npcm7xx_clk_pll_class_init, 1044 }; 1045 1046 static const TypeInfo npcm7xx_clk_sel_info = { 1047 .name = TYPE_NPCM7XX_CLOCK_SEL, 1048 .parent = TYPE_DEVICE, 1049 .instance_size = sizeof(NPCM7xxClockSELState), 1050 .instance_init = npcm7xx_clk_sel_init, 1051 .class_init = npcm7xx_clk_sel_class_init, 1052 }; 1053 1054 static const TypeInfo npcm7xx_clk_divider_info = { 1055 .name = TYPE_NPCM7XX_CLOCK_DIVIDER, 1056 .parent = TYPE_DEVICE, 1057 .instance_size = sizeof(NPCM7xxClockDividerState), 1058 .instance_init = npcm7xx_clk_divider_init, 1059 .class_init = npcm7xx_clk_divider_class_init, 1060 }; 1061 1062 static const TypeInfo npcm7xx_clk_info = { 1063 .name = TYPE_NPCM7XX_CLK, 1064 .parent = TYPE_SYS_BUS_DEVICE, 1065 .instance_size = sizeof(NPCM7xxCLKState), 1066 .instance_init = npcm7xx_clk_init, 1067 .class_init = npcm7xx_clk_class_init, 1068 }; 1069 1070 static void npcm7xx_clk_register_type(void) 1071 { 1072 type_register_static(&npcm7xx_clk_pll_info); 1073 type_register_static(&npcm7xx_clk_sel_info); 1074 type_register_static(&npcm7xx_clk_divider_info); 1075 type_register_static(&npcm7xx_clk_info); 1076 } 1077 type_init(npcm7xx_clk_register_type); 1078