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_pll_init(Object
*obj
)
591 NPCM7xxClockPLLState
*pll
= NPCM7XX_CLOCK_PLL(obj
);
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");
598 static void npcm7xx_clk_sel_init(Object
*obj
)
601 NPCM7xxClockSELState
*sel
= NPCM7XX_CLOCK_SEL(obj
);
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
);
608 sel
->clock_out
= qdev_init_clock_out(DEVICE(sel
), "clock-out");
610 static void npcm7xx_clk_divider_init(Object
*obj
)
612 NPCM7xxClockDividerState
*div
= NPCM7XX_CLOCK_DIVIDER(obj
);
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");
619 static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState
*pll
,
620 NPCM7xxCLKState
*clk
, const PLLInitInfo
*init_info
)
622 pll
->name
= init_info
->name
;
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
);
631 static void npcm7xx_init_clock_sel(NPCM7xxClockSELState
*sel
,
632 NPCM7xxCLKState
*clk
, const SELInitInfo
*init_info
)
634 int input_size
= init_info
->input_size
;
636 sel
->name
= init_info
->name
;
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
);
648 static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState
*div
,
649 NPCM7xxCLKState
*clk
, const DividerInitInfo
*init_info
)
651 div
->name
= init_info
->name
;
654 div
->divide
= init_info
->divide
;
655 if (div
->divide
== divide_by_constant
) {
656 div
->divisor
= init_info
->divisor
;
658 div
->reg
= init_info
->reg
;
659 div
->offset
= init_info
->offset
;
660 div
->len
= init_info
->len
;
662 if (init_info
->public_name
!= NULL
) {
663 qdev_alias_clock(DEVICE(div
), "clock-out", DEVICE(clk
),
664 init_info
->public_name
);
668 static Clock
*npcm7xx_get_clock(NPCM7xxCLKState
*clk
, ClockSrcType type
,
675 return clk
->plls
[index
].clock_out
;
677 return clk
->sels
[index
].clock_out
;
679 return clk
->dividers
[index
].clock_out
;
681 g_assert_not_reached();
685 static void npcm7xx_connect_clocks(NPCM7xxCLKState
*clk
)
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
);
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
);
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
);
709 static uint64_t npcm7xx_clk_read(void *opaque
, hwaddr offset
, unsigned size
)
711 uint32_t reg
= offset
/ sizeof(uint32_t);
712 NPCM7xxCLKState
*s
= opaque
;
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",
724 case NPCM7XX_CLK_SWRSTR
:
725 qemu_log_mask(LOG_GUEST_ERROR
,
726 "%s: register @ 0x%04" HWADDR_PRIx
" is write-only\n",
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
;
735 case NPCM7XX_CLK_CNTR25M
:
736 now_ns
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
738 * This register counts 25 MHz cycles, updating every 640 ns. It rolls
739 * over to zero every second.
741 * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000.
743 value
= (((now_ns
- s
->ref_ns
) / 640) << 4) % NPCM7XX_CLOCK_REF_HZ
;
747 value
= s
->regs
[reg
];
751 trace_npcm7xx_clk_read(offset
, value
);
756 static void npcm7xx_clk_write(void *opaque
, hwaddr offset
,
757 uint64_t v
, unsigned size
)
759 uint32_t reg
= offset
/ sizeof(uint32_t);
760 NPCM7xxCLKState
*s
= opaque
;
763 trace_npcm7xx_clk_write(offset
, value
);
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",
773 case NPCM7XX_CLK_SWRSTR
:
774 qemu_log_mask(LOG_UNIMP
, "%s: SW reset not implemented: 0x%02x\n",
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
;
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
;
794 value
|= (value
& PLLCON_LOKS
);
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
)]);
803 case NPCM7XX_CLK_CLKSEL
:
804 npcm7xx_clk_update_all_sels(s
);
807 case NPCM7XX_CLK_CLKDIV1
:
808 case NPCM7XX_CLK_CLKDIV2
:
809 case NPCM7XX_CLK_CLKDIV3
:
810 npcm7xx_clk_update_all_dividers(s
);
813 case NPCM7XX_CLK_CNTR25M
:
814 qemu_log_mask(LOG_GUEST_ERROR
,
815 "%s: register @ 0x%04" HWADDR_PRIx
" is read-only\n",
820 s
->regs
[reg
] = value
;
823 /* Perform reset action triggered by a watchdog */
824 static void npcm7xx_clk_perform_watchdog_reset(void *opaque
, int n
,
827 NPCM7xxCLKState
*clk
= NPCM7XX_CLK(opaque
);
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();
835 qemu_log_mask(LOG_UNIMP
,
836 "%s: only CPU reset is implemented. (requested 0x%" PRIx32
")\n",
841 static const struct MemoryRegionOps npcm7xx_clk_ops
= {
842 .read
= npcm7xx_clk_read
,
843 .write
= npcm7xx_clk_write
,
844 .endianness
= DEVICE_LITTLE_ENDIAN
,
846 .min_access_size
= 4,
847 .max_access_size
= 4,
852 static void npcm7xx_clk_enter_reset(Object
*obj
, ResetType type
)
854 NPCM7xxCLKState
*s
= NPCM7XX_CLK(obj
);
856 QEMU_BUILD_BUG_ON(sizeof(s
->regs
) != sizeof(cold_reset_values
));
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
);
867 * A small number of registers need to be reset on a core domain reset,
868 * but no such reset type exists yet.
870 qemu_log_mask(LOG_UNIMP
, "%s: reset type %d not implemented.",
874 static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState
*s
)
878 s
->clkref
= qdev_init_clock_in(DEVICE(s
), "clkref", NULL
, NULL
);
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
]);
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
]);
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
]);
904 /* Second pass: connect converter modules */
905 npcm7xx_connect_clocks(s
);
907 clock_update_hz(s
->clkref
, NPCM7XX_CLOCK_REF_HZ
);
910 static void npcm7xx_clk_init(Object
*obj
)
912 NPCM7xxCLKState
*s
= NPCM7XX_CLK(obj
);
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
);
919 static int npcm7xx_clk_post_load(void *opaque
, int version_id
)
921 if (version_id
>= 1) {
922 NPCM7xxCLKState
*clk
= opaque
;
924 npcm7xx_clk_update_all_clocks(clk
);
930 static void npcm7xx_clk_realize(DeviceState
*dev
, Error
**errp
)
933 NPCM7xxCLKState
*s
= NPCM7XX_CLK(dev
);
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
);
939 /* Realize child devices */
940 for (i
= 0; i
< NPCM7XX_CLOCK_NR_PLLS
; ++i
) {
941 if (!qdev_realize(DEVICE(&s
->plls
[i
]), NULL
, errp
)) {
945 for (i
= 0; i
< NPCM7XX_CLOCK_NR_SELS
; ++i
) {
946 if (!qdev_realize(DEVICE(&s
->sels
[i
]), NULL
, errp
)) {
950 for (i
= 0; i
< NPCM7XX_CLOCK_NR_DIVIDERS
; ++i
) {
951 if (!qdev_realize(DEVICE(&s
->dividers
[i
]), NULL
, errp
)) {
957 static const VMStateDescription vmstate_npcm7xx_clk_pll
= {
958 .name
= "npcm7xx-clock-pll",
960 .minimum_version_id
= 0,
961 .fields
= (VMStateField
[]) {
962 VMSTATE_CLOCK(clock_in
, NPCM7xxClockPLLState
),
963 VMSTATE_END_OF_LIST(),
967 static const VMStateDescription vmstate_npcm7xx_clk_sel
= {
968 .name
= "npcm7xx-clock-sel",
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(),
978 static const VMStateDescription vmstate_npcm7xx_clk_divider
= {
979 .name
= "npcm7xx-clock-divider",
981 .minimum_version_id
= 0,
982 .fields
= (VMStateField
[]) {
983 VMSTATE_CLOCK(clock_in
, NPCM7xxClockDividerState
),
984 VMSTATE_END_OF_LIST(),
988 static const VMStateDescription vmstate_npcm7xx_clk
= {
989 .name
= "npcm7xx-clk",
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(),
1001 static void npcm7xx_clk_pll_class_init(ObjectClass
*klass
, void *data
)
1003 DeviceClass
*dc
= DEVICE_CLASS(klass
);
1005 dc
->desc
= "NPCM7xx Clock PLL Module";
1006 dc
->vmsd
= &vmstate_npcm7xx_clk_pll
;
1009 static void npcm7xx_clk_sel_class_init(ObjectClass
*klass
, void *data
)
1011 DeviceClass
*dc
= DEVICE_CLASS(klass
);
1013 dc
->desc
= "NPCM7xx Clock SEL Module";
1014 dc
->vmsd
= &vmstate_npcm7xx_clk_sel
;
1017 static void npcm7xx_clk_divider_class_init(ObjectClass
*klass
, void *data
)
1019 DeviceClass
*dc
= DEVICE_CLASS(klass
);
1021 dc
->desc
= "NPCM7xx Clock Divider Module";
1022 dc
->vmsd
= &vmstate_npcm7xx_clk_divider
;
1025 static void npcm7xx_clk_class_init(ObjectClass
*klass
, void *data
)
1027 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
1028 DeviceClass
*dc
= DEVICE_CLASS(klass
);
1030 QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END
> NPCM7XX_CLK_NR_REGS
);
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
;
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
,
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
,
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
,
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
,
1070 static void npcm7xx_clk_register_type(void)
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
);
1077 type_init(npcm7xx_clk_register_type
);