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