2 * Nuvoton NPCM7xx Clock Control Registers.
4 * Copyright 2020 Google LLC
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.
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
17 #include "qemu/osdep.h"
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"
25 #include "qemu/module.h"
26 #include "qemu/timer.h"
27 #include "qemu/units.h"
29 #include "sysemu/watchdog.h"
32 * The reference clock hz, and the SECCNT and CNTR25M registers in this module,
35 #define NPCM7XX_CLOCK_REF_HZ (25000000)
37 /* Register Field Definitions */
38 #define NPCM7XX_CLK_WDRCR_CA9C BIT(0) /* Cortex-A9 Cores */
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)
48 enum NPCM7xxCLKRegisters
{
55 NPCM7XX_CLK_IPSRST1
= 0x20 / sizeof(uint32_t),
79 * These reset values were taken from version 0.91 of the NPCM750R data sheet.
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.
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,
106 /* The number of watchdogs that can trigger a reset. */
107 #define NPCM7XX_NR_WATCHDOGS (3)
109 /* Clock converter functions */
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)
121 static void npcm7xx_clk_update_pll(void *opaque
)
123 NPCM7xxClockPLLState
*s
= opaque
;
124 uint32_t con
= s
->clk
->regs
[s
->reg
];
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
);
136 clock_update_hz(s
->clock_out
, freq
);
139 static void npcm7xx_clk_update_sel(void *opaque
)
141 NPCM7xxClockSELState
*s
= opaque
;
142 uint32_t index
= extract32(s
->clk
->regs
[NPCM7XX_CLK_CLKSEL
], s
->offset
,
145 if (index
>= s
->input_size
) {
146 qemu_log_mask(LOG_GUEST_ERROR
,
147 "%s: SEL index: %u out of range\n",
151 clock_update_hz(s
->clock_out
, clock_get_hz(s
->clock_in
[index
]));
154 static void npcm7xx_clk_update_divider(void *opaque
)
156 NPCM7xxClockDividerState
*s
= opaque
;
160 clock_update_hz(s
->clock_out
, freq
);
163 static uint32_t divide_by_constant(NPCM7xxClockDividerState
*s
)
165 return clock_get_hz(s
->clock_in
) / s
->divisor
;
168 static uint32_t divide_by_reg_divisor(NPCM7xxClockDividerState
*s
)
170 return clock_get_hz(s
->clock_in
) /
171 (extract32(s
->clk
->regs
[s
->reg
], s
->offset
, s
->len
) + 1);
174 static uint32_t divide_by_reg_divisor_times_2(NPCM7xxClockDividerState
*s
)
176 return divide_by_reg_divisor(s
) / 2;
179 static uint32_t shift_by_reg_divisor(NPCM7xxClockDividerState
*s
)
181 return clock_get_hz(s
->clock_in
) >>
182 extract32(s
->clk
->regs
[s
->reg
], s
->offset
, s
->len
);
185 static NPCM7xxClockPLL
find_pll_by_reg(enum NPCM7xxCLKRegisters 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
;
197 g_assert_not_reached();
201 static void npcm7xx_clk_update_all_plls(NPCM7xxCLKState
*clk
)
205 for (i
= 0; i
< NPCM7XX_CLOCK_NR_PLLS
; ++i
) {
206 npcm7xx_clk_update_pll(&clk
->plls
[i
]);
210 static void npcm7xx_clk_update_all_sels(NPCM7xxCLKState
*clk
)
214 for (i
= 0; i
< NPCM7XX_CLOCK_NR_SELS
; ++i
) {
215 npcm7xx_clk_update_sel(&clk
->sels
[i
]);
219 static void npcm7xx_clk_update_all_dividers(NPCM7xxCLKState
*clk
)
223 for (i
= 0; i
< NPCM7XX_CLOCK_NR_DIVIDERS
; ++i
) {
224 npcm7xx_clk_update_divider(&clk
->dividers
[i
]);
228 static void npcm7xx_clk_update_all_clocks(NPCM7xxCLKState
*clk
)
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
);
236 /* Types of clock sources. */
237 typedef enum ClockSrcType
{
244 typedef struct PLLInitInfo
{
246 ClockSrcType src_type
;
249 const char *public_name
;
252 typedef struct SELInitInfo
{
255 ClockSrcType src_type
[NPCM7XX_CLK_SEL_MAX_INPUT
];
256 int src_index
[NPCM7XX_CLK_SEL_MAX_INPUT
];
259 const char *public_name
;
262 typedef struct DividerInitInfo
{
264 ClockSrcType src_type
;
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
;
274 static const PLLInitInfo pll_init_info_list
[] = {
275 [NPCM7XX_CLOCK_PLL0
] = {
277 .src_type
= CLKSRC_REF
,
278 .reg
= NPCM7XX_CLK_PLLCON0
,
280 [NPCM7XX_CLOCK_PLL1
] = {
282 .src_type
= CLKSRC_REF
,
283 .reg
= NPCM7XX_CLK_PLLCON1
,
285 [NPCM7XX_CLOCK_PLL2
] = {
287 .src_type
= CLKSRC_REF
,
288 .reg
= NPCM7XX_CLK_PLLCON2
,
290 [NPCM7XX_CLOCK_PLLG
] = {
292 .src_type
= CLKSRC_REF
,
293 .reg
= NPCM7XX_CLK_PLLCONG
,
297 static const SELInitInfo sel_init_info_list
[] = {
298 [NPCM7XX_CLOCK_PIXCKSEL
] = {
301 .src_type
= {CLKSRC_PLL
, CLKSRC_REF
},
302 .src_index
= {NPCM7XX_CLOCK_PLLG
, 0},
305 .public_name
= "pixel-clock",
307 [NPCM7XX_CLOCK_MCCKSEL
] = {
310 .src_type
= {CLKSRC_DIV
, CLKSRC_REF
, CLKSRC_REF
,
311 /*MCBPCK, shouldn't be used in normal operation*/
313 .src_index
= {NPCM7XX_CLOCK_PLL1D2
, 0, 0, 0},
316 .public_name
= "mc-phy-clock",
318 [NPCM7XX_CLOCK_CPUCKSEL
] = {
321 .src_type
= {CLKSRC_PLL
, CLKSRC_DIV
, CLKSRC_REF
,
322 /*SYSBPCK, shouldn't be used in normal operation*/
324 .src_index
= {NPCM7XX_CLOCK_PLL0
, NPCM7XX_CLOCK_PLL1D2
, 0, 0},
327 .public_name
= "system-clock",
329 [NPCM7XX_CLOCK_CLKOUTSEL
] = {
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
},
338 .public_name
= "tock",
340 [NPCM7XX_CLOCK_UARTCKSEL
] = {
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
},
349 [NPCM7XX_CLOCK_TIMCKSEL
] = {
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
},
358 [NPCM7XX_CLOCK_SDCKSEL
] = {
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
},
367 [NPCM7XX_CLOCK_GFXMSEL
] = {
370 .src_type
= {CLKSRC_REF
, CLKSRC_PLL
},
371 .src_index
= {0, NPCM7XX_CLOCK_PLL2
},
375 [NPCM7XX_CLOCK_SUCKSEL
] = {
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
},
386 static const DividerInitInfo divider_init_info_list
[] = {
387 [NPCM7XX_CLOCK_PLL1D2
] = {
389 .src_type
= CLKSRC_PLL
,
390 .src_index
= NPCM7XX_CLOCK_PLL1
,
391 .divide
= divide_by_constant
,
394 [NPCM7XX_CLOCK_PLL2D2
] = {
396 .src_type
= CLKSRC_PLL
,
397 .src_index
= NPCM7XX_CLOCK_PLL2
,
398 .divide
= divide_by_constant
,
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
,
407 .public_name
= "mc-clock"
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
,
417 .public_name
= "clk2"
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
,
427 .public_name
= "clk4"
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
,
437 .public_name
= "ahb3-spi3-clock"
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
,
447 .public_name
= "spi0-clock",
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
,
457 .public_name
= "spix-clock",
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
,
467 .public_name
= "apb1-clock",
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
,
477 .public_name
= "apb2-clock",
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
,
487 .public_name
= "apb3-clock",
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
,
497 .public_name
= "apb4-clock",
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
,
507 .public_name
= "apb5-clock",
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
,
517 .public_name
= "clkout",
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
,
527 .public_name
= "uart-clock",
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
,
537 .public_name
= "timer-clock",
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
,
547 .public_name
= "adc-clock",
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
,
557 .public_name
= "mmc-clock",
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
,
567 .public_name
= "sdhc-clock",
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
,
575 .public_name
= "gfxm-clock",
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
,
585 .public_name
= "utmi-clock",
589 static void npcm7xx_clk_update_pll_cb(void *opaque
, ClockEvent event
)
591 npcm7xx_clk_update_pll(opaque
);
594 static void npcm7xx_clk_pll_init(Object
*obj
)
596 NPCM7xxClockPLLState
*pll
= NPCM7XX_CLOCK_PLL(obj
);
598 pll
->clock_in
= qdev_init_clock_in(DEVICE(pll
), "clock-in",
599 npcm7xx_clk_update_pll_cb
, pll
,
601 pll
->clock_out
= qdev_init_clock_out(DEVICE(pll
), "clock-out");
604 static void npcm7xx_clk_update_sel_cb(void *opaque
, ClockEvent event
)
606 npcm7xx_clk_update_sel(opaque
);
609 static void npcm7xx_clk_sel_init(Object
*obj
)
612 NPCM7xxClockSELState
*sel
= NPCM7XX_CLOCK_SEL(obj
);
614 for (i
= 0; i
< NPCM7XX_CLK_SEL_MAX_INPUT
; ++i
) {
615 g_autofree
char *s
= g_strdup_printf("clock-in[%d]", i
);
616 sel
->clock_in
[i
] = qdev_init_clock_in(DEVICE(sel
), s
,
617 npcm7xx_clk_update_sel_cb
, sel
, ClockUpdate
);
619 sel
->clock_out
= qdev_init_clock_out(DEVICE(sel
), "clock-out");
622 static void npcm7xx_clk_update_divider_cb(void *opaque
, ClockEvent event
)
624 npcm7xx_clk_update_divider(opaque
);
627 static void npcm7xx_clk_divider_init(Object
*obj
)
629 NPCM7xxClockDividerState
*div
= NPCM7XX_CLOCK_DIVIDER(obj
);
631 div
->clock_in
= qdev_init_clock_in(DEVICE(div
), "clock-in",
632 npcm7xx_clk_update_divider_cb
,
634 div
->clock_out
= qdev_init_clock_out(DEVICE(div
), "clock-out");
637 static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState
*pll
,
638 NPCM7xxCLKState
*clk
, const PLLInitInfo
*init_info
)
640 pll
->name
= init_info
->name
;
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
);
649 static void npcm7xx_init_clock_sel(NPCM7xxClockSELState
*sel
,
650 NPCM7xxCLKState
*clk
, const SELInitInfo
*init_info
)
652 int input_size
= init_info
->input_size
;
654 sel
->name
= init_info
->name
;
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
);
666 static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState
*div
,
667 NPCM7xxCLKState
*clk
, const DividerInitInfo
*init_info
)
669 div
->name
= init_info
->name
;
672 div
->divide
= init_info
->divide
;
673 if (div
->divide
== divide_by_constant
) {
674 div
->divisor
= init_info
->divisor
;
676 div
->reg
= init_info
->reg
;
677 div
->offset
= init_info
->offset
;
678 div
->len
= init_info
->len
;
680 if (init_info
->public_name
!= NULL
) {
681 qdev_alias_clock(DEVICE(div
), "clock-out", DEVICE(clk
),
682 init_info
->public_name
);
686 static Clock
*npcm7xx_get_clock(NPCM7xxCLKState
*clk
, ClockSrcType type
,
693 return clk
->plls
[index
].clock_out
;
695 return clk
->sels
[index
].clock_out
;
697 return clk
->dividers
[index
].clock_out
;
699 g_assert_not_reached();
703 static void npcm7xx_connect_clocks(NPCM7xxCLKState
*clk
)
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
);
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
);
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
);
727 static uint64_t npcm7xx_clk_read(void *opaque
, hwaddr offset
, unsigned size
)
729 uint32_t reg
= offset
/ sizeof(uint32_t);
730 NPCM7xxCLKState
*s
= opaque
;
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",
742 case NPCM7XX_CLK_SWRSTR
:
743 qemu_log_mask(LOG_GUEST_ERROR
,
744 "%s: register @ 0x%04" HWADDR_PRIx
" is write-only\n",
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
;
753 case NPCM7XX_CLK_CNTR25M
:
754 now_ns
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
756 * This register counts 25 MHz cycles, updating every 640 ns. It rolls
757 * over to zero every second.
759 * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000.
761 value
= (((now_ns
- s
->ref_ns
) / 640) << 4) % NPCM7XX_CLOCK_REF_HZ
;
765 value
= s
->regs
[reg
];
769 trace_npcm7xx_clk_read(offset
, value
);
774 static void npcm7xx_clk_write(void *opaque
, hwaddr offset
,
775 uint64_t v
, unsigned size
)
777 uint32_t reg
= offset
/ sizeof(uint32_t);
778 NPCM7xxCLKState
*s
= opaque
;
781 trace_npcm7xx_clk_write(offset
, value
);
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",
791 case NPCM7XX_CLK_SWRSTR
:
792 qemu_log_mask(LOG_UNIMP
, "%s: SW reset not implemented: 0x%02x\n",
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
;
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
;
812 value
|= (value
& PLLCON_LOKS
);
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
)]);
821 case NPCM7XX_CLK_CLKSEL
:
822 npcm7xx_clk_update_all_sels(s
);
825 case NPCM7XX_CLK_CLKDIV1
:
826 case NPCM7XX_CLK_CLKDIV2
:
827 case NPCM7XX_CLK_CLKDIV3
:
828 npcm7xx_clk_update_all_dividers(s
);
831 case NPCM7XX_CLK_CNTR25M
:
832 qemu_log_mask(LOG_GUEST_ERROR
,
833 "%s: register @ 0x%04" HWADDR_PRIx
" is read-only\n",
838 s
->regs
[reg
] = value
;
841 /* Perform reset action triggered by a watchdog */
842 static void npcm7xx_clk_perform_watchdog_reset(void *opaque
, int n
,
845 NPCM7xxCLKState
*clk
= NPCM7XX_CLK(opaque
);
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();
853 qemu_log_mask(LOG_UNIMP
,
854 "%s: only CPU reset is implemented. (requested 0x%" PRIx32
")\n",
859 static const struct MemoryRegionOps npcm7xx_clk_ops
= {
860 .read
= npcm7xx_clk_read
,
861 .write
= npcm7xx_clk_write
,
862 .endianness
= DEVICE_LITTLE_ENDIAN
,
864 .min_access_size
= 4,
865 .max_access_size
= 4,
870 static void npcm7xx_clk_enter_reset(Object
*obj
, ResetType type
)
872 NPCM7xxCLKState
*s
= NPCM7XX_CLK(obj
);
874 QEMU_BUILD_BUG_ON(sizeof(s
->regs
) != sizeof(cold_reset_values
));
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
);
885 * A small number of registers need to be reset on a core domain reset,
886 * but no such reset type exists yet.
888 qemu_log_mask(LOG_UNIMP
, "%s: reset type %d not implemented.",
892 static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState
*s
)
896 s
->clkref
= qdev_init_clock_in(DEVICE(s
), "clkref", NULL
, NULL
, 0);
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
]);
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
]);
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
]);
922 /* Second pass: connect converter modules */
923 npcm7xx_connect_clocks(s
);
925 clock_update_hz(s
->clkref
, NPCM7XX_CLOCK_REF_HZ
);
928 static void npcm7xx_clk_init(Object
*obj
)
930 NPCM7xxCLKState
*s
= NPCM7XX_CLK(obj
);
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
);
937 static int npcm7xx_clk_post_load(void *opaque
, int version_id
)
939 if (version_id
>= 1) {
940 NPCM7xxCLKState
*clk
= opaque
;
942 npcm7xx_clk_update_all_clocks(clk
);
948 static void npcm7xx_clk_realize(DeviceState
*dev
, Error
**errp
)
951 NPCM7xxCLKState
*s
= NPCM7XX_CLK(dev
);
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
);
957 /* Realize child devices */
958 for (i
= 0; i
< NPCM7XX_CLOCK_NR_PLLS
; ++i
) {
959 if (!qdev_realize(DEVICE(&s
->plls
[i
]), NULL
, errp
)) {
963 for (i
= 0; i
< NPCM7XX_CLOCK_NR_SELS
; ++i
) {
964 if (!qdev_realize(DEVICE(&s
->sels
[i
]), NULL
, errp
)) {
968 for (i
= 0; i
< NPCM7XX_CLOCK_NR_DIVIDERS
; ++i
) {
969 if (!qdev_realize(DEVICE(&s
->dividers
[i
]), NULL
, errp
)) {
975 static const VMStateDescription vmstate_npcm7xx_clk_pll
= {
976 .name
= "npcm7xx-clock-pll",
978 .minimum_version_id
= 0,
979 .fields
= (const VMStateField
[]) {
980 VMSTATE_CLOCK(clock_in
, NPCM7xxClockPLLState
),
981 VMSTATE_END_OF_LIST(),
985 static const VMStateDescription vmstate_npcm7xx_clk_sel
= {
986 .name
= "npcm7xx-clock-sel",
988 .minimum_version_id
= 0,
989 .fields
= (const 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(),
996 static const VMStateDescription vmstate_npcm7xx_clk_divider
= {
997 .name
= "npcm7xx-clock-divider",
999 .minimum_version_id
= 0,
1000 .fields
= (const VMStateField
[]) {
1001 VMSTATE_CLOCK(clock_in
, NPCM7xxClockDividerState
),
1002 VMSTATE_END_OF_LIST(),
1006 static const VMStateDescription vmstate_npcm7xx_clk
= {
1007 .name
= "npcm7xx-clk",
1009 .minimum_version_id
= 1,
1010 .post_load
= npcm7xx_clk_post_load
,
1011 .fields
= (const 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(),
1019 static void npcm7xx_clk_pll_class_init(ObjectClass
*klass
, void *data
)
1021 DeviceClass
*dc
= DEVICE_CLASS(klass
);
1023 dc
->desc
= "NPCM7xx Clock PLL Module";
1024 dc
->vmsd
= &vmstate_npcm7xx_clk_pll
;
1027 static void npcm7xx_clk_sel_class_init(ObjectClass
*klass
, void *data
)
1029 DeviceClass
*dc
= DEVICE_CLASS(klass
);
1031 dc
->desc
= "NPCM7xx Clock SEL Module";
1032 dc
->vmsd
= &vmstate_npcm7xx_clk_sel
;
1035 static void npcm7xx_clk_divider_class_init(ObjectClass
*klass
, void *data
)
1037 DeviceClass
*dc
= DEVICE_CLASS(klass
);
1039 dc
->desc
= "NPCM7xx Clock Divider Module";
1040 dc
->vmsd
= &vmstate_npcm7xx_clk_divider
;
1043 static void npcm7xx_clk_class_init(ObjectClass
*klass
, void *data
)
1045 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
1046 DeviceClass
*dc
= DEVICE_CLASS(klass
);
1048 QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END
> NPCM7XX_CLK_NR_REGS
);
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
;
1056 static 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
,
1064 static 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
,
1072 static 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
,
1080 static 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
,
1088 static void npcm7xx_clk_register_type(void)
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
);
1095 type_init(npcm7xx_clk_register_type
);