1c8283b0fSHao Wu /*
24e67d50dSHao Wu * Nuvoton NPCM7xx/8xx Clock Control Registers.
3c8283b0fSHao Wu *
4c8283b0fSHao Wu * Copyright 2020 Google LLC
5c8283b0fSHao Wu *
6c8283b0fSHao Wu * This program is free software; you can redistribute it and/or modify it
7c8283b0fSHao Wu * under the terms of the GNU General Public License as published by the
8c8283b0fSHao Wu * Free Software Foundation; either version 2 of the License, or
9c8283b0fSHao Wu * (at your option) any later version.
10c8283b0fSHao Wu *
11c8283b0fSHao Wu * This program is distributed in the hope that it will be useful, but WITHOUT
12c8283b0fSHao Wu * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13c8283b0fSHao Wu * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14c8283b0fSHao Wu * for more details.
15c8283b0fSHao Wu */
16c8283b0fSHao Wu
17c8283b0fSHao Wu #include "qemu/osdep.h"
18c8283b0fSHao Wu
19c8283b0fSHao Wu #include "hw/misc/npcm_clk.h"
20c8283b0fSHao Wu #include "hw/timer/npcm7xx_timer.h"
21c8283b0fSHao Wu #include "hw/qdev-clock.h"
22c8283b0fSHao Wu #include "migration/vmstate.h"
23c8283b0fSHao Wu #include "qemu/error-report.h"
24c8283b0fSHao Wu #include "qemu/log.h"
25c8283b0fSHao Wu #include "qemu/module.h"
26c8283b0fSHao Wu #include "qemu/timer.h"
27c8283b0fSHao Wu #include "qemu/units.h"
28c8283b0fSHao Wu #include "trace.h"
29c8283b0fSHao Wu #include "system/watchdog.h"
30c8283b0fSHao Wu
31c8283b0fSHao Wu /*
32c8283b0fSHao Wu * The reference clock hz, and the SECCNT and CNTR25M registers in this module,
33c8283b0fSHao Wu * is always 25 MHz.
34c8283b0fSHao Wu */
35c8283b0fSHao Wu #define NPCM7XX_CLOCK_REF_HZ (25000000)
36c8283b0fSHao Wu
37c8283b0fSHao Wu /* Register Field Definitions */
38c8283b0fSHao Wu #define NPCM7XX_CLK_WDRCR_CA9C BIT(0) /* Cortex-A9 Cores */
39c8283b0fSHao Wu
40c8283b0fSHao Wu #define PLLCON_LOKI BIT(31)
41c8283b0fSHao Wu #define PLLCON_LOKS BIT(30)
42c8283b0fSHao Wu #define PLLCON_PWDEN BIT(12)
43c8283b0fSHao Wu #define PLLCON_FBDV(con) extract32((con), 16, 12)
44c8283b0fSHao Wu #define PLLCON_OTDV2(con) extract32((con), 13, 3)
45c8283b0fSHao Wu #define PLLCON_OTDV1(con) extract32((con), 8, 3)
46c8283b0fSHao Wu #define PLLCON_INDV(con) extract32((con), 0, 6)
47c8283b0fSHao Wu
48c8283b0fSHao Wu enum NPCM7xxCLKRegisters {
49c8283b0fSHao Wu NPCM7XX_CLK_CLKEN1,
50c8283b0fSHao Wu NPCM7XX_CLK_CLKSEL,
51c8283b0fSHao Wu NPCM7XX_CLK_CLKDIV1,
52c8283b0fSHao Wu NPCM7XX_CLK_PLLCON0,
53c8283b0fSHao Wu NPCM7XX_CLK_PLLCON1,
54c8283b0fSHao Wu NPCM7XX_CLK_SWRSTR,
55c8283b0fSHao Wu NPCM7XX_CLK_IPSRST1 = 0x20 / sizeof(uint32_t),
56c8283b0fSHao Wu NPCM7XX_CLK_IPSRST2,
57c8283b0fSHao Wu NPCM7XX_CLK_CLKEN2,
58c8283b0fSHao Wu NPCM7XX_CLK_CLKDIV2,
59c8283b0fSHao Wu NPCM7XX_CLK_CLKEN3,
60c8283b0fSHao Wu NPCM7XX_CLK_IPSRST3,
61c8283b0fSHao Wu NPCM7XX_CLK_WD0RCR,
62c8283b0fSHao Wu NPCM7XX_CLK_WD1RCR,
63c8283b0fSHao Wu NPCM7XX_CLK_WD2RCR,
64c8283b0fSHao Wu NPCM7XX_CLK_SWRSTC1,
65c8283b0fSHao Wu NPCM7XX_CLK_SWRSTC2,
66c8283b0fSHao Wu NPCM7XX_CLK_SWRSTC3,
67c8283b0fSHao Wu NPCM7XX_CLK_SWRSTC4,
68c8283b0fSHao Wu NPCM7XX_CLK_PLLCON2,
69c8283b0fSHao Wu NPCM7XX_CLK_CLKDIV3,
70c8283b0fSHao Wu NPCM7XX_CLK_CORSTC,
71c8283b0fSHao Wu NPCM7XX_CLK_PLLCONG,
72c8283b0fSHao Wu NPCM7XX_CLK_AHBCKFI,
73c8283b0fSHao Wu NPCM7XX_CLK_SECCNT,
74c8283b0fSHao Wu NPCM7XX_CLK_CNTR25M,
754e67d50dSHao Wu };
764e67d50dSHao Wu
774e67d50dSHao Wu enum NPCM8xxCLKRegisters {
784e67d50dSHao Wu NPCM8XX_CLK_CLKEN1,
794e67d50dSHao Wu NPCM8XX_CLK_CLKSEL,
804e67d50dSHao Wu NPCM8XX_CLK_CLKDIV1,
814e67d50dSHao Wu NPCM8XX_CLK_PLLCON0,
824e67d50dSHao Wu NPCM8XX_CLK_PLLCON1,
834e67d50dSHao Wu NPCM8XX_CLK_SWRSTR,
844e67d50dSHao Wu NPCM8XX_CLK_IPSRST1 = 0x20 / sizeof(uint32_t),
854e67d50dSHao Wu NPCM8XX_CLK_IPSRST2,
864e67d50dSHao Wu NPCM8XX_CLK_CLKEN2,
874e67d50dSHao Wu NPCM8XX_CLK_CLKDIV2,
884e67d50dSHao Wu NPCM8XX_CLK_CLKEN3,
894e67d50dSHao Wu NPCM8XX_CLK_IPSRST3,
904e67d50dSHao Wu NPCM8XX_CLK_WD0RCR,
914e67d50dSHao Wu NPCM8XX_CLK_WD1RCR,
924e67d50dSHao Wu NPCM8XX_CLK_WD2RCR,
934e67d50dSHao Wu NPCM8XX_CLK_SWRSTC1,
944e67d50dSHao Wu NPCM8XX_CLK_SWRSTC2,
954e67d50dSHao Wu NPCM8XX_CLK_SWRSTC3,
964e67d50dSHao Wu NPCM8XX_CLK_TIPRSTC,
974e67d50dSHao Wu NPCM8XX_CLK_PLLCON2,
984e67d50dSHao Wu NPCM8XX_CLK_CLKDIV3,
994e67d50dSHao Wu NPCM8XX_CLK_CORSTC,
1004e67d50dSHao Wu NPCM8XX_CLK_PLLCONG,
1014e67d50dSHao Wu NPCM8XX_CLK_AHBCKFI,
1024e67d50dSHao Wu NPCM8XX_CLK_SECCNT,
1034e67d50dSHao Wu NPCM8XX_CLK_CNTR25M,
1044e67d50dSHao Wu /* Registers unique to NPCM8XX SoC */
1054e67d50dSHao Wu NPCM8XX_CLK_CLKEN4,
1064e67d50dSHao Wu NPCM8XX_CLK_IPSRST4,
1074e67d50dSHao Wu NPCM8XX_CLK_BUSTO,
1084e67d50dSHao Wu NPCM8XX_CLK_CLKDIV4,
1094e67d50dSHao Wu NPCM8XX_CLK_WD0RCRB,
1104e67d50dSHao Wu NPCM8XX_CLK_WD1RCRB,
1114e67d50dSHao Wu NPCM8XX_CLK_WD2RCRB,
1124e67d50dSHao Wu NPCM8XX_CLK_SWRSTC1B,
1134e67d50dSHao Wu NPCM8XX_CLK_SWRSTC2B,
1144e67d50dSHao Wu NPCM8XX_CLK_SWRSTC3B,
1154e67d50dSHao Wu NPCM8XX_CLK_TIPRSTCB,
1164e67d50dSHao Wu NPCM8XX_CLK_CORSTCB,
1174e67d50dSHao Wu NPCM8XX_CLK_IPSRSTDIS1,
1184e67d50dSHao Wu NPCM8XX_CLK_IPSRSTDIS2,
1194e67d50dSHao Wu NPCM8XX_CLK_IPSRSTDIS3,
1204e67d50dSHao Wu NPCM8XX_CLK_IPSRSTDIS4,
1214e67d50dSHao Wu NPCM8XX_CLK_CLKENDIS1,
1224e67d50dSHao Wu NPCM8XX_CLK_CLKENDIS2,
1234e67d50dSHao Wu NPCM8XX_CLK_CLKENDIS3,
1244e67d50dSHao Wu NPCM8XX_CLK_CLKENDIS4,
1254e67d50dSHao Wu NPCM8XX_CLK_THRTL_CNT,
126c8283b0fSHao Wu };
127c8283b0fSHao Wu
128c8283b0fSHao Wu /*
129c8283b0fSHao Wu * These reset values were taken from version 0.91 of the NPCM750R data sheet.
130c8283b0fSHao Wu *
131c8283b0fSHao Wu * All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on
132c8283b0fSHao Wu * core domain reset, but this reset type is not yet supported by QEMU.
133c8283b0fSHao Wu */
134cf76c4e1SHao Wu static const uint32_t npcm7xx_cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
135c8283b0fSHao Wu [NPCM7XX_CLK_CLKEN1] = 0xffffffff,
136c8283b0fSHao Wu [NPCM7XX_CLK_CLKSEL] = 0x004aaaaa,
137c8283b0fSHao Wu [NPCM7XX_CLK_CLKDIV1] = 0x5413f855,
138c8283b0fSHao Wu [NPCM7XX_CLK_PLLCON0] = 0x00222101 | PLLCON_LOKI,
139c8283b0fSHao Wu [NPCM7XX_CLK_PLLCON1] = 0x00202101 | PLLCON_LOKI,
140c8283b0fSHao Wu [NPCM7XX_CLK_IPSRST1] = 0x00001000,
141c8283b0fSHao Wu [NPCM7XX_CLK_IPSRST2] = 0x80000000,
142c8283b0fSHao Wu [NPCM7XX_CLK_CLKEN2] = 0xffffffff,
143c8283b0fSHao Wu [NPCM7XX_CLK_CLKDIV2] = 0xaa4f8f9f,
144c8283b0fSHao Wu [NPCM7XX_CLK_CLKEN3] = 0xffffffff,
145c8283b0fSHao Wu [NPCM7XX_CLK_IPSRST3] = 0x03000000,
146c8283b0fSHao Wu [NPCM7XX_CLK_WD0RCR] = 0xffffffff,
147c8283b0fSHao Wu [NPCM7XX_CLK_WD1RCR] = 0xffffffff,
148c8283b0fSHao Wu [NPCM7XX_CLK_WD2RCR] = 0xffffffff,
149c8283b0fSHao Wu [NPCM7XX_CLK_SWRSTC1] = 0x00000003,
150c8283b0fSHao Wu [NPCM7XX_CLK_PLLCON2] = 0x00c02105 | PLLCON_LOKI,
151c8283b0fSHao Wu [NPCM7XX_CLK_CORSTC] = 0x04000003,
152c8283b0fSHao Wu [NPCM7XX_CLK_PLLCONG] = 0x01228606 | PLLCON_LOKI,
153c8283b0fSHao Wu [NPCM7XX_CLK_AHBCKFI] = 0x000000c8,
154c8283b0fSHao Wu };
155c8283b0fSHao Wu
1564e67d50dSHao Wu /*
1574e67d50dSHao Wu * These reset values were taken from version 0.92 of the NPCM8xx data sheet.
1584e67d50dSHao Wu */
1594e67d50dSHao Wu static const uint32_t npcm8xx_cold_reset_values[NPCM8XX_CLK_NR_REGS] = {
1604e67d50dSHao Wu [NPCM8XX_CLK_CLKEN1] = 0xffffffff,
1614e67d50dSHao Wu [NPCM8XX_CLK_CLKSEL] = 0x154aaaaa,
1624e67d50dSHao Wu [NPCM8XX_CLK_CLKDIV1] = 0x5413f855,
1634e67d50dSHao Wu [NPCM8XX_CLK_PLLCON0] = 0x00222101 | PLLCON_LOKI,
1644e67d50dSHao Wu [NPCM8XX_CLK_PLLCON1] = 0x00202101 | PLLCON_LOKI,
1654e67d50dSHao Wu [NPCM8XX_CLK_IPSRST1] = 0x00001000,
1664e67d50dSHao Wu [NPCM8XX_CLK_IPSRST2] = 0x80000000,
1674e67d50dSHao Wu [NPCM8XX_CLK_CLKEN2] = 0xffffffff,
1684e67d50dSHao Wu [NPCM8XX_CLK_CLKDIV2] = 0xaa4f8f9f,
1694e67d50dSHao Wu [NPCM8XX_CLK_CLKEN3] = 0xffffffff,
1704e67d50dSHao Wu [NPCM8XX_CLK_IPSRST3] = 0x03000000,
1714e67d50dSHao Wu [NPCM8XX_CLK_WD0RCR] = 0xffffffff,
1724e67d50dSHao Wu [NPCM8XX_CLK_WD1RCR] = 0xffffffff,
1734e67d50dSHao Wu [NPCM8XX_CLK_WD2RCR] = 0xffffffff,
1744e67d50dSHao Wu [NPCM8XX_CLK_SWRSTC1] = 0x00000003,
1754e67d50dSHao Wu [NPCM8XX_CLK_SWRSTC2] = 0x00000001,
1764e67d50dSHao Wu [NPCM8XX_CLK_SWRSTC3] = 0x00000001,
1774e67d50dSHao Wu [NPCM8XX_CLK_TIPRSTC] = 0x00000001,
1784e67d50dSHao Wu [NPCM8XX_CLK_PLLCON2] = 0x00c02105 | PLLCON_LOKI,
1794e67d50dSHao Wu [NPCM8XX_CLK_CLKDIV3] = 0x00009100,
1804e67d50dSHao Wu [NPCM8XX_CLK_CORSTC] = 0x04000003,
1814e67d50dSHao Wu [NPCM8XX_CLK_PLLCONG] = 0x01228606 | PLLCON_LOKI,
1824e67d50dSHao Wu [NPCM8XX_CLK_AHBCKFI] = 0x000000c8,
1834e67d50dSHao Wu [NPCM8XX_CLK_CLKEN4] = 0xffffffff,
1844e67d50dSHao Wu [NPCM8XX_CLK_CLKDIV4] = 0x70009000,
1854e67d50dSHao Wu [NPCM8XX_CLK_IPSRST4] = 0x02000000,
1864e67d50dSHao Wu [NPCM8XX_CLK_WD0RCRB] = 0xfffffe71,
1874e67d50dSHao Wu [NPCM8XX_CLK_WD1RCRB] = 0xfffffe71,
1884e67d50dSHao Wu [NPCM8XX_CLK_WD2RCRB] = 0xfffffe71,
1894e67d50dSHao Wu [NPCM8XX_CLK_SWRSTC1B] = 0xfffffe71,
1904e67d50dSHao Wu [NPCM8XX_CLK_SWRSTC2B] = 0xfffffe71,
1914e67d50dSHao Wu [NPCM8XX_CLK_SWRSTC3B] = 0xfffffe71,
1924e67d50dSHao Wu [NPCM8XX_CLK_TIPRSTCB] = 0xfffffe71,
1934e67d50dSHao Wu [NPCM8XX_CLK_CORSTCB] = 0xfffffe71,
1944e67d50dSHao Wu };
1954e67d50dSHao Wu
196c8283b0fSHao Wu /* The number of watchdogs that can trigger a reset. */
197c8283b0fSHao Wu #define NPCM7XX_NR_WATCHDOGS (3)
198c8283b0fSHao Wu
199c8283b0fSHao Wu /* Clock converter functions */
200c8283b0fSHao Wu
201c8283b0fSHao Wu #define TYPE_NPCM7XX_CLOCK_PLL "npcm7xx-clock-pll"
202c8283b0fSHao Wu #define NPCM7XX_CLOCK_PLL(obj) OBJECT_CHECK(NPCM7xxClockPLLState, \
203c8283b0fSHao Wu (obj), TYPE_NPCM7XX_CLOCK_PLL)
204c8283b0fSHao Wu #define TYPE_NPCM7XX_CLOCK_SEL "npcm7xx-clock-sel"
205c8283b0fSHao Wu #define NPCM7XX_CLOCK_SEL(obj) OBJECT_CHECK(NPCM7xxClockSELState, \
206c8283b0fSHao Wu (obj), TYPE_NPCM7XX_CLOCK_SEL)
207c8283b0fSHao Wu #define TYPE_NPCM7XX_CLOCK_DIVIDER "npcm7xx-clock-divider"
208c8283b0fSHao Wu #define NPCM7XX_CLOCK_DIVIDER(obj) OBJECT_CHECK(NPCM7xxClockDividerState, \
209c8283b0fSHao Wu (obj), TYPE_NPCM7XX_CLOCK_DIVIDER)
210c8283b0fSHao Wu
npcm7xx_clk_update_pll(void * opaque)211c8283b0fSHao Wu static void npcm7xx_clk_update_pll(void *opaque)
212c8283b0fSHao Wu {
213c8283b0fSHao Wu NPCM7xxClockPLLState *s = opaque;
214c8283b0fSHao Wu uint32_t con = s->clk->regs[s->reg];
215c8283b0fSHao Wu uint64_t freq;
216c8283b0fSHao Wu
217c8283b0fSHao Wu /* The PLL is grounded if it is not locked yet. */
218c8283b0fSHao Wu if (con & PLLCON_LOKI) {
219c8283b0fSHao Wu freq = clock_get_hz(s->clock_in);
220c8283b0fSHao Wu freq *= PLLCON_FBDV(con);
221c8283b0fSHao Wu freq /= PLLCON_INDV(con) * PLLCON_OTDV1(con) * PLLCON_OTDV2(con);
222c8283b0fSHao Wu } else {
223c8283b0fSHao Wu freq = 0;
224c8283b0fSHao Wu }
225c8283b0fSHao Wu
226c8283b0fSHao Wu clock_update_hz(s->clock_out, freq);
227c8283b0fSHao Wu }
228c8283b0fSHao Wu
npcm7xx_clk_update_sel(void * opaque)229c8283b0fSHao Wu static void npcm7xx_clk_update_sel(void *opaque)
230c8283b0fSHao Wu {
231c8283b0fSHao Wu NPCM7xxClockSELState *s = opaque;
232c8283b0fSHao Wu uint32_t index = extract32(s->clk->regs[NPCM7XX_CLK_CLKSEL], s->offset,
233c8283b0fSHao Wu s->len);
234c8283b0fSHao Wu
235c8283b0fSHao Wu if (index >= s->input_size) {
236c8283b0fSHao Wu qemu_log_mask(LOG_GUEST_ERROR,
237c8283b0fSHao Wu "%s: SEL index: %u out of range\n",
238c8283b0fSHao Wu __func__, index);
239c8283b0fSHao Wu index = 0;
240c8283b0fSHao Wu }
241c8283b0fSHao Wu clock_update_hz(s->clock_out, clock_get_hz(s->clock_in[index]));
242c8283b0fSHao Wu }
243c8283b0fSHao Wu
npcm7xx_clk_update_divider(void * opaque)244c8283b0fSHao Wu static void npcm7xx_clk_update_divider(void *opaque)
245c8283b0fSHao Wu {
246c8283b0fSHao Wu NPCM7xxClockDividerState *s = opaque;
247c8283b0fSHao Wu uint32_t freq;
248c8283b0fSHao Wu
249c8283b0fSHao Wu freq = s->divide(s);
250c8283b0fSHao Wu clock_update_hz(s->clock_out, freq);
251c8283b0fSHao Wu }
252c8283b0fSHao Wu
divide_by_constant(NPCM7xxClockDividerState * s)253c8283b0fSHao Wu static uint32_t divide_by_constant(NPCM7xxClockDividerState *s)
254c8283b0fSHao Wu {
255c8283b0fSHao Wu return clock_get_hz(s->clock_in) / s->divisor;
256c8283b0fSHao Wu }
257c8283b0fSHao Wu
divide_by_reg_divisor(NPCM7xxClockDividerState * s)258c8283b0fSHao Wu static uint32_t divide_by_reg_divisor(NPCM7xxClockDividerState *s)
259c8283b0fSHao Wu {
260c8283b0fSHao Wu return clock_get_hz(s->clock_in) /
261c8283b0fSHao Wu (extract32(s->clk->regs[s->reg], s->offset, s->len) + 1);
262c8283b0fSHao Wu }
263c8283b0fSHao Wu
divide_by_reg_divisor_times_2(NPCM7xxClockDividerState * s)264c8283b0fSHao Wu static uint32_t divide_by_reg_divisor_times_2(NPCM7xxClockDividerState *s)
265c8283b0fSHao Wu {
266c8283b0fSHao Wu return divide_by_reg_divisor(s) / 2;
267c8283b0fSHao Wu }
268c8283b0fSHao Wu
shift_by_reg_divisor(NPCM7xxClockDividerState * s)269c8283b0fSHao Wu static uint32_t shift_by_reg_divisor(NPCM7xxClockDividerState *s)
270c8283b0fSHao Wu {
271c8283b0fSHao Wu return clock_get_hz(s->clock_in) >>
272c8283b0fSHao Wu extract32(s->clk->regs[s->reg], s->offset, s->len);
273c8283b0fSHao Wu }
274c8283b0fSHao Wu
find_pll_by_reg(enum NPCM7xxCLKRegisters reg)275c8283b0fSHao Wu static NPCM7xxClockPLL find_pll_by_reg(enum NPCM7xxCLKRegisters reg)
276c8283b0fSHao Wu {
277c8283b0fSHao Wu switch (reg) {
278c8283b0fSHao Wu case NPCM7XX_CLK_PLLCON0:
279c8283b0fSHao Wu return NPCM7XX_CLOCK_PLL0;
280c8283b0fSHao Wu case NPCM7XX_CLK_PLLCON1:
281c8283b0fSHao Wu return NPCM7XX_CLOCK_PLL1;
282c8283b0fSHao Wu case NPCM7XX_CLK_PLLCON2:
283c8283b0fSHao Wu return NPCM7XX_CLOCK_PLL2;
284c8283b0fSHao Wu case NPCM7XX_CLK_PLLCONG:
285c8283b0fSHao Wu return NPCM7XX_CLOCK_PLLG;
286c8283b0fSHao Wu default:
287c8283b0fSHao Wu g_assert_not_reached();
288c8283b0fSHao Wu }
289c8283b0fSHao Wu }
290c8283b0fSHao Wu
npcm7xx_clk_update_all_plls(NPCMCLKState * clk)291ca6d6a94SHao Wu static void npcm7xx_clk_update_all_plls(NPCMCLKState *clk)
292c8283b0fSHao Wu {
293c8283b0fSHao Wu int i;
294c8283b0fSHao Wu
295c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) {
296c8283b0fSHao Wu npcm7xx_clk_update_pll(&clk->plls[i]);
297c8283b0fSHao Wu }
298c8283b0fSHao Wu }
299c8283b0fSHao Wu
npcm7xx_clk_update_all_sels(NPCMCLKState * clk)300ca6d6a94SHao Wu static void npcm7xx_clk_update_all_sels(NPCMCLKState *clk)
301c8283b0fSHao Wu {
302c8283b0fSHao Wu int i;
303c8283b0fSHao Wu
304c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) {
305c8283b0fSHao Wu npcm7xx_clk_update_sel(&clk->sels[i]);
306c8283b0fSHao Wu }
307c8283b0fSHao Wu }
308c8283b0fSHao Wu
npcm7xx_clk_update_all_dividers(NPCMCLKState * clk)309ca6d6a94SHao Wu static void npcm7xx_clk_update_all_dividers(NPCMCLKState *clk)
310c8283b0fSHao Wu {
311c8283b0fSHao Wu int i;
312c8283b0fSHao Wu
313c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) {
314c8283b0fSHao Wu npcm7xx_clk_update_divider(&clk->dividers[i]);
315c8283b0fSHao Wu }
316c8283b0fSHao Wu }
317c8283b0fSHao Wu
npcm7xx_clk_update_all_clocks(NPCMCLKState * clk)318ca6d6a94SHao Wu static void npcm7xx_clk_update_all_clocks(NPCMCLKState *clk)
319c8283b0fSHao Wu {
320c8283b0fSHao Wu clock_update_hz(clk->clkref, NPCM7XX_CLOCK_REF_HZ);
321c8283b0fSHao Wu npcm7xx_clk_update_all_plls(clk);
322c8283b0fSHao Wu npcm7xx_clk_update_all_sels(clk);
323c8283b0fSHao Wu npcm7xx_clk_update_all_dividers(clk);
324c8283b0fSHao Wu }
325c8283b0fSHao Wu
326c8283b0fSHao Wu /* Types of clock sources. */
327c8283b0fSHao Wu typedef enum ClockSrcType {
328c8283b0fSHao Wu CLKSRC_REF,
329c8283b0fSHao Wu CLKSRC_PLL,
330c8283b0fSHao Wu CLKSRC_SEL,
331c8283b0fSHao Wu CLKSRC_DIV,
332c8283b0fSHao Wu } ClockSrcType;
333c8283b0fSHao Wu
334c8283b0fSHao Wu typedef struct PLLInitInfo {
335c8283b0fSHao Wu const char *name;
336c8283b0fSHao Wu ClockSrcType src_type;
337c8283b0fSHao Wu int src_index;
338c8283b0fSHao Wu int reg;
339c8283b0fSHao Wu const char *public_name;
340c8283b0fSHao Wu } PLLInitInfo;
341c8283b0fSHao Wu
342c8283b0fSHao Wu typedef struct SELInitInfo {
343c8283b0fSHao Wu const char *name;
344c8283b0fSHao Wu uint8_t input_size;
345c8283b0fSHao Wu ClockSrcType src_type[NPCM7XX_CLK_SEL_MAX_INPUT];
346c8283b0fSHao Wu int src_index[NPCM7XX_CLK_SEL_MAX_INPUT];
347c8283b0fSHao Wu int offset;
348c8283b0fSHao Wu int len;
349c8283b0fSHao Wu const char *public_name;
350c8283b0fSHao Wu } SELInitInfo;
351c8283b0fSHao Wu
352c8283b0fSHao Wu typedef struct DividerInitInfo {
353c8283b0fSHao Wu const char *name;
354c8283b0fSHao Wu ClockSrcType src_type;
355c8283b0fSHao Wu int src_index;
356c8283b0fSHao Wu uint32_t (*divide)(NPCM7xxClockDividerState *s);
357c8283b0fSHao Wu int reg; /* not used when type == CONSTANT */
358c8283b0fSHao Wu int offset; /* not used when type == CONSTANT */
359c8283b0fSHao Wu int len; /* not used when type == CONSTANT */
360c8283b0fSHao Wu int divisor; /* used only when type == CONSTANT */
361c8283b0fSHao Wu const char *public_name;
362c8283b0fSHao Wu } DividerInitInfo;
363c8283b0fSHao Wu
364c8283b0fSHao Wu static const PLLInitInfo pll_init_info_list[] = {
365c8283b0fSHao Wu [NPCM7XX_CLOCK_PLL0] = {
366c8283b0fSHao Wu .name = "pll0",
367c8283b0fSHao Wu .src_type = CLKSRC_REF,
368c8283b0fSHao Wu .reg = NPCM7XX_CLK_PLLCON0,
369c8283b0fSHao Wu },
370c8283b0fSHao Wu [NPCM7XX_CLOCK_PLL1] = {
371c8283b0fSHao Wu .name = "pll1",
372c8283b0fSHao Wu .src_type = CLKSRC_REF,
373c8283b0fSHao Wu .reg = NPCM7XX_CLK_PLLCON1,
374c8283b0fSHao Wu },
375c8283b0fSHao Wu [NPCM7XX_CLOCK_PLL2] = {
376c8283b0fSHao Wu .name = "pll2",
377c8283b0fSHao Wu .src_type = CLKSRC_REF,
378c8283b0fSHao Wu .reg = NPCM7XX_CLK_PLLCON2,
379c8283b0fSHao Wu },
380c8283b0fSHao Wu [NPCM7XX_CLOCK_PLLG] = {
381c8283b0fSHao Wu .name = "pllg",
382c8283b0fSHao Wu .src_type = CLKSRC_REF,
383c8283b0fSHao Wu .reg = NPCM7XX_CLK_PLLCONG,
384c8283b0fSHao Wu },
385c8283b0fSHao Wu };
386c8283b0fSHao Wu
387c8283b0fSHao Wu static const SELInitInfo sel_init_info_list[] = {
388c8283b0fSHao Wu [NPCM7XX_CLOCK_PIXCKSEL] = {
389c8283b0fSHao Wu .name = "pixcksel",
390c8283b0fSHao Wu .input_size = 2,
391c8283b0fSHao Wu .src_type = {CLKSRC_PLL, CLKSRC_REF},
392c8283b0fSHao Wu .src_index = {NPCM7XX_CLOCK_PLLG, 0},
393c8283b0fSHao Wu .offset = 5,
394c8283b0fSHao Wu .len = 1,
395c8283b0fSHao Wu .public_name = "pixel-clock",
396c8283b0fSHao Wu },
397c8283b0fSHao Wu [NPCM7XX_CLOCK_MCCKSEL] = {
398c8283b0fSHao Wu .name = "mccksel",
399c8283b0fSHao Wu .input_size = 4,
400c8283b0fSHao Wu .src_type = {CLKSRC_DIV, CLKSRC_REF, CLKSRC_REF,
401c8283b0fSHao Wu /*MCBPCK, shouldn't be used in normal operation*/
402c8283b0fSHao Wu CLKSRC_REF},
403c8283b0fSHao Wu .src_index = {NPCM7XX_CLOCK_PLL1D2, 0, 0, 0},
404c8283b0fSHao Wu .offset = 12,
405c8283b0fSHao Wu .len = 2,
406c8283b0fSHao Wu .public_name = "mc-phy-clock",
407c8283b0fSHao Wu },
408c8283b0fSHao Wu [NPCM7XX_CLOCK_CPUCKSEL] = {
409c8283b0fSHao Wu .name = "cpucksel",
410c8283b0fSHao Wu .input_size = 4,
411c8283b0fSHao Wu .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF,
412c8283b0fSHao Wu /*SYSBPCK, shouldn't be used in normal operation*/
413c8283b0fSHao Wu CLKSRC_REF},
414c8283b0fSHao Wu .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, 0},
415c8283b0fSHao Wu .offset = 0,
416c8283b0fSHao Wu .len = 2,
417c8283b0fSHao Wu .public_name = "system-clock",
418c8283b0fSHao Wu },
419c8283b0fSHao Wu [NPCM7XX_CLOCK_CLKOUTSEL] = {
420c8283b0fSHao Wu .name = "clkoutsel",
421c8283b0fSHao Wu .input_size = 5,
422c8283b0fSHao Wu .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF,
423c8283b0fSHao Wu CLKSRC_PLL, CLKSRC_DIV},
424c8283b0fSHao Wu .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0,
425c8283b0fSHao Wu NPCM7XX_CLOCK_PLLG, NPCM7XX_CLOCK_PLL2D2},
426c8283b0fSHao Wu .offset = 18,
427c8283b0fSHao Wu .len = 3,
428c8283b0fSHao Wu .public_name = "tock",
429c8283b0fSHao Wu },
430c8283b0fSHao Wu [NPCM7XX_CLOCK_UARTCKSEL] = {
431c8283b0fSHao Wu .name = "uartcksel",
432c8283b0fSHao Wu .input_size = 4,
433c8283b0fSHao Wu .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV},
434c8283b0fSHao Wu .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0,
435c8283b0fSHao Wu NPCM7XX_CLOCK_PLL2D2},
436c8283b0fSHao Wu .offset = 8,
437c8283b0fSHao Wu .len = 2,
438c8283b0fSHao Wu },
439c8283b0fSHao Wu [NPCM7XX_CLOCK_TIMCKSEL] = {
440c8283b0fSHao Wu .name = "timcksel",
441c8283b0fSHao Wu .input_size = 4,
442c8283b0fSHao Wu .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV},
443c8283b0fSHao Wu .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0,
444c8283b0fSHao Wu NPCM7XX_CLOCK_PLL2D2},
445c8283b0fSHao Wu .offset = 14,
446c8283b0fSHao Wu .len = 2,
447c8283b0fSHao Wu },
448c8283b0fSHao Wu [NPCM7XX_CLOCK_SDCKSEL] = {
449c8283b0fSHao Wu .name = "sdcksel",
450c8283b0fSHao Wu .input_size = 4,
451c8283b0fSHao Wu .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV},
452c8283b0fSHao Wu .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0,
453c8283b0fSHao Wu NPCM7XX_CLOCK_PLL2D2},
454c8283b0fSHao Wu .offset = 6,
455c8283b0fSHao Wu .len = 2,
456c8283b0fSHao Wu },
457c8283b0fSHao Wu [NPCM7XX_CLOCK_GFXMSEL] = {
458c8283b0fSHao Wu .name = "gfxmksel",
459c8283b0fSHao Wu .input_size = 2,
460c8283b0fSHao Wu .src_type = {CLKSRC_REF, CLKSRC_PLL},
461c8283b0fSHao Wu .src_index = {0, NPCM7XX_CLOCK_PLL2},
462c8283b0fSHao Wu .offset = 21,
463c8283b0fSHao Wu .len = 1,
464c8283b0fSHao Wu },
465c8283b0fSHao Wu [NPCM7XX_CLOCK_SUCKSEL] = {
466c8283b0fSHao Wu .name = "sucksel",
467c8283b0fSHao Wu .input_size = 4,
468c8283b0fSHao Wu .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV},
469c8283b0fSHao Wu .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0,
470c8283b0fSHao Wu NPCM7XX_CLOCK_PLL2D2},
471c8283b0fSHao Wu .offset = 10,
472c8283b0fSHao Wu .len = 2,
473c8283b0fSHao Wu },
474c8283b0fSHao Wu };
475c8283b0fSHao Wu
476c8283b0fSHao Wu static const DividerInitInfo divider_init_info_list[] = {
477c8283b0fSHao Wu [NPCM7XX_CLOCK_PLL1D2] = {
478c8283b0fSHao Wu .name = "pll1d2",
479c8283b0fSHao Wu .src_type = CLKSRC_PLL,
480c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_PLL1,
481c8283b0fSHao Wu .divide = divide_by_constant,
482c8283b0fSHao Wu .divisor = 2,
483c8283b0fSHao Wu },
484c8283b0fSHao Wu [NPCM7XX_CLOCK_PLL2D2] = {
485c8283b0fSHao Wu .name = "pll2d2",
486c8283b0fSHao Wu .src_type = CLKSRC_PLL,
487c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_PLL2,
488c8283b0fSHao Wu .divide = divide_by_constant,
489c8283b0fSHao Wu .divisor = 2,
490c8283b0fSHao Wu },
491c8283b0fSHao Wu [NPCM7XX_CLOCK_MC_DIVIDER] = {
492c8283b0fSHao Wu .name = "mc-divider",
493c8283b0fSHao Wu .src_type = CLKSRC_SEL,
494c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_MCCKSEL,
495c8283b0fSHao Wu .divide = divide_by_constant,
496c8283b0fSHao Wu .divisor = 2,
497c8283b0fSHao Wu .public_name = "mc-clock"
498c8283b0fSHao Wu },
499c8283b0fSHao Wu [NPCM7XX_CLOCK_AXI_DIVIDER] = {
500c8283b0fSHao Wu .name = "axi-divider",
501c8283b0fSHao Wu .src_type = CLKSRC_SEL,
502c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_CPUCKSEL,
503c8283b0fSHao Wu .divide = shift_by_reg_divisor,
504c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV1,
505c8283b0fSHao Wu .offset = 0,
506c8283b0fSHao Wu .len = 1,
507c8283b0fSHao Wu .public_name = "clk2"
508c8283b0fSHao Wu },
509c8283b0fSHao Wu [NPCM7XX_CLOCK_AHB_DIVIDER] = {
510c8283b0fSHao Wu .name = "ahb-divider",
511c8283b0fSHao Wu .src_type = CLKSRC_DIV,
512c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_AXI_DIVIDER,
513c8283b0fSHao Wu .divide = divide_by_reg_divisor,
514c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV1,
515c8283b0fSHao Wu .offset = 26,
516c8283b0fSHao Wu .len = 2,
517c8283b0fSHao Wu .public_name = "clk4"
518c8283b0fSHao Wu },
519c8283b0fSHao Wu [NPCM7XX_CLOCK_AHB3_DIVIDER] = {
520c8283b0fSHao Wu .name = "ahb3-divider",
521c8283b0fSHao Wu .src_type = CLKSRC_DIV,
522c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
523c8283b0fSHao Wu .divide = divide_by_reg_divisor,
524c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV1,
525c8283b0fSHao Wu .offset = 6,
526c8283b0fSHao Wu .len = 5,
527c8283b0fSHao Wu .public_name = "ahb3-spi3-clock"
528c8283b0fSHao Wu },
529c8283b0fSHao Wu [NPCM7XX_CLOCK_SPI0_DIVIDER] = {
530c8283b0fSHao Wu .name = "spi0-divider",
531c8283b0fSHao Wu .src_type = CLKSRC_DIV,
532c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
533c8283b0fSHao Wu .divide = divide_by_reg_divisor,
534c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV3,
535c8283b0fSHao Wu .offset = 6,
536c8283b0fSHao Wu .len = 5,
537c8283b0fSHao Wu .public_name = "spi0-clock",
538c8283b0fSHao Wu },
539c8283b0fSHao Wu [NPCM7XX_CLOCK_SPIX_DIVIDER] = {
540c8283b0fSHao Wu .name = "spix-divider",
541c8283b0fSHao Wu .src_type = CLKSRC_DIV,
542c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
543c8283b0fSHao Wu .divide = divide_by_reg_divisor,
544c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV3,
545c8283b0fSHao Wu .offset = 1,
546c8283b0fSHao Wu .len = 5,
547c8283b0fSHao Wu .public_name = "spix-clock",
548c8283b0fSHao Wu },
549c8283b0fSHao Wu [NPCM7XX_CLOCK_APB1_DIVIDER] = {
550c8283b0fSHao Wu .name = "apb1-divider",
551c8283b0fSHao Wu .src_type = CLKSRC_DIV,
552c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
553c8283b0fSHao Wu .divide = shift_by_reg_divisor,
554c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV2,
555c8283b0fSHao Wu .offset = 24,
556c8283b0fSHao Wu .len = 2,
557c8283b0fSHao Wu .public_name = "apb1-clock",
558c8283b0fSHao Wu },
559c8283b0fSHao Wu [NPCM7XX_CLOCK_APB2_DIVIDER] = {
560c8283b0fSHao Wu .name = "apb2-divider",
561c8283b0fSHao Wu .src_type = CLKSRC_DIV,
562c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
563c8283b0fSHao Wu .divide = shift_by_reg_divisor,
564c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV2,
565c8283b0fSHao Wu .offset = 26,
566c8283b0fSHao Wu .len = 2,
567c8283b0fSHao Wu .public_name = "apb2-clock",
568c8283b0fSHao Wu },
569c8283b0fSHao Wu [NPCM7XX_CLOCK_APB3_DIVIDER] = {
570c8283b0fSHao Wu .name = "apb3-divider",
571c8283b0fSHao Wu .src_type = CLKSRC_DIV,
572c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
573c8283b0fSHao Wu .divide = shift_by_reg_divisor,
574c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV2,
575c8283b0fSHao Wu .offset = 28,
576c8283b0fSHao Wu .len = 2,
577c8283b0fSHao Wu .public_name = "apb3-clock",
578c8283b0fSHao Wu },
579c8283b0fSHao Wu [NPCM7XX_CLOCK_APB4_DIVIDER] = {
580c8283b0fSHao Wu .name = "apb4-divider",
581c8283b0fSHao Wu .src_type = CLKSRC_DIV,
582c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
583c8283b0fSHao Wu .divide = shift_by_reg_divisor,
584c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV2,
585c8283b0fSHao Wu .offset = 30,
586c8283b0fSHao Wu .len = 2,
587c8283b0fSHao Wu .public_name = "apb4-clock",
588c8283b0fSHao Wu },
589c8283b0fSHao Wu [NPCM7XX_CLOCK_APB5_DIVIDER] = {
590c8283b0fSHao Wu .name = "apb5-divider",
591c8283b0fSHao Wu .src_type = CLKSRC_DIV,
592c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_AHB_DIVIDER,
593c8283b0fSHao Wu .divide = shift_by_reg_divisor,
594c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV2,
595c8283b0fSHao Wu .offset = 22,
596c8283b0fSHao Wu .len = 2,
597c8283b0fSHao Wu .public_name = "apb5-clock",
598c8283b0fSHao Wu },
599c8283b0fSHao Wu [NPCM7XX_CLOCK_CLKOUT_DIVIDER] = {
600c8283b0fSHao Wu .name = "clkout-divider",
601c8283b0fSHao Wu .src_type = CLKSRC_SEL,
602c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_CLKOUTSEL,
603c8283b0fSHao Wu .divide = divide_by_reg_divisor,
604c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV2,
605c8283b0fSHao Wu .offset = 16,
606c8283b0fSHao Wu .len = 5,
607c8283b0fSHao Wu .public_name = "clkout",
608c8283b0fSHao Wu },
609c8283b0fSHao Wu [NPCM7XX_CLOCK_UART_DIVIDER] = {
610c8283b0fSHao Wu .name = "uart-divider",
611c8283b0fSHao Wu .src_type = CLKSRC_SEL,
612c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_UARTCKSEL,
613c8283b0fSHao Wu .divide = divide_by_reg_divisor,
614c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV1,
615c8283b0fSHao Wu .offset = 16,
616c8283b0fSHao Wu .len = 5,
617c8283b0fSHao Wu .public_name = "uart-clock",
618c8283b0fSHao Wu },
619c8283b0fSHao Wu [NPCM7XX_CLOCK_TIMER_DIVIDER] = {
620c8283b0fSHao Wu .name = "timer-divider",
621c8283b0fSHao Wu .src_type = CLKSRC_SEL,
622c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_TIMCKSEL,
623c8283b0fSHao Wu .divide = divide_by_reg_divisor,
624c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV1,
625c8283b0fSHao Wu .offset = 21,
626c8283b0fSHao Wu .len = 5,
627c8283b0fSHao Wu .public_name = "timer-clock",
628c8283b0fSHao Wu },
629c8283b0fSHao Wu [NPCM7XX_CLOCK_ADC_DIVIDER] = {
630c8283b0fSHao Wu .name = "adc-divider",
631c8283b0fSHao Wu .src_type = CLKSRC_DIV,
632c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_TIMER_DIVIDER,
633c8283b0fSHao Wu .divide = shift_by_reg_divisor,
634c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV1,
635c8283b0fSHao Wu .offset = 28,
636c8283b0fSHao Wu .len = 3,
637c8283b0fSHao Wu .public_name = "adc-clock",
638c8283b0fSHao Wu },
639c8283b0fSHao Wu [NPCM7XX_CLOCK_MMC_DIVIDER] = {
640c8283b0fSHao Wu .name = "mmc-divider",
641c8283b0fSHao Wu .src_type = CLKSRC_SEL,
642c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_SDCKSEL,
643c8283b0fSHao Wu .divide = divide_by_reg_divisor,
644c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV1,
645c8283b0fSHao Wu .offset = 11,
646c8283b0fSHao Wu .len = 5,
647c8283b0fSHao Wu .public_name = "mmc-clock",
648c8283b0fSHao Wu },
649c8283b0fSHao Wu [NPCM7XX_CLOCK_SDHC_DIVIDER] = {
650c8283b0fSHao Wu .name = "sdhc-divider",
651c8283b0fSHao Wu .src_type = CLKSRC_SEL,
652c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_SDCKSEL,
653c8283b0fSHao Wu .divide = divide_by_reg_divisor_times_2,
654c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV2,
655c8283b0fSHao Wu .offset = 0,
656c8283b0fSHao Wu .len = 4,
657c8283b0fSHao Wu .public_name = "sdhc-clock",
658c8283b0fSHao Wu },
659c8283b0fSHao Wu [NPCM7XX_CLOCK_GFXM_DIVIDER] = {
660c8283b0fSHao Wu .name = "gfxm-divider",
661c8283b0fSHao Wu .src_type = CLKSRC_SEL,
662c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_GFXMSEL,
663c8283b0fSHao Wu .divide = divide_by_constant,
664c8283b0fSHao Wu .divisor = 3,
665c8283b0fSHao Wu .public_name = "gfxm-clock",
666c8283b0fSHao Wu },
667c8283b0fSHao Wu [NPCM7XX_CLOCK_UTMI_DIVIDER] = {
668c8283b0fSHao Wu .name = "utmi-divider",
669c8283b0fSHao Wu .src_type = CLKSRC_SEL,
670c8283b0fSHao Wu .src_index = NPCM7XX_CLOCK_SUCKSEL,
671c8283b0fSHao Wu .divide = divide_by_reg_divisor,
672c8283b0fSHao Wu .reg = NPCM7XX_CLK_CLKDIV2,
673c8283b0fSHao Wu .offset = 8,
674c8283b0fSHao Wu .len = 5,
675c8283b0fSHao Wu .public_name = "utmi-clock",
676c8283b0fSHao Wu },
677c8283b0fSHao Wu };
678c8283b0fSHao Wu
npcm7xx_clk_update_pll_cb(void * opaque,ClockEvent event)679c8283b0fSHao Wu static void npcm7xx_clk_update_pll_cb(void *opaque, ClockEvent event)
680c8283b0fSHao Wu {
681c8283b0fSHao Wu npcm7xx_clk_update_pll(opaque);
682c8283b0fSHao Wu }
683c8283b0fSHao Wu
npcm7xx_clk_pll_init(Object * obj)684c8283b0fSHao Wu static void npcm7xx_clk_pll_init(Object *obj)
685c8283b0fSHao Wu {
686c8283b0fSHao Wu NPCM7xxClockPLLState *pll = NPCM7XX_CLOCK_PLL(obj);
687c8283b0fSHao Wu
688c8283b0fSHao Wu pll->clock_in = qdev_init_clock_in(DEVICE(pll), "clock-in",
689c8283b0fSHao Wu npcm7xx_clk_update_pll_cb, pll,
690c8283b0fSHao Wu ClockUpdate);
691c8283b0fSHao Wu pll->clock_out = qdev_init_clock_out(DEVICE(pll), "clock-out");
692c8283b0fSHao Wu }
693c8283b0fSHao Wu
npcm7xx_clk_update_sel_cb(void * opaque,ClockEvent event)694c8283b0fSHao Wu static void npcm7xx_clk_update_sel_cb(void *opaque, ClockEvent event)
695c8283b0fSHao Wu {
696c8283b0fSHao Wu npcm7xx_clk_update_sel(opaque);
697c8283b0fSHao Wu }
698c8283b0fSHao Wu
npcm7xx_clk_sel_init(Object * obj)699c8283b0fSHao Wu static void npcm7xx_clk_sel_init(Object *obj)
700c8283b0fSHao Wu {
701c8283b0fSHao Wu int i;
702c8283b0fSHao Wu NPCM7xxClockSELState *sel = NPCM7XX_CLOCK_SEL(obj);
703c8283b0fSHao Wu
704c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLK_SEL_MAX_INPUT; ++i) {
705c8283b0fSHao Wu g_autofree char *s = g_strdup_printf("clock-in[%d]", i);
706c8283b0fSHao Wu sel->clock_in[i] = qdev_init_clock_in(DEVICE(sel), s,
707c8283b0fSHao Wu npcm7xx_clk_update_sel_cb, sel, ClockUpdate);
708c8283b0fSHao Wu }
709c8283b0fSHao Wu sel->clock_out = qdev_init_clock_out(DEVICE(sel), "clock-out");
710c8283b0fSHao Wu }
711c8283b0fSHao Wu
npcm7xx_clk_update_divider_cb(void * opaque,ClockEvent event)712c8283b0fSHao Wu static void npcm7xx_clk_update_divider_cb(void *opaque, ClockEvent event)
713c8283b0fSHao Wu {
714c8283b0fSHao Wu npcm7xx_clk_update_divider(opaque);
715c8283b0fSHao Wu }
716c8283b0fSHao Wu
npcm7xx_clk_divider_init(Object * obj)717c8283b0fSHao Wu static void npcm7xx_clk_divider_init(Object *obj)
718c8283b0fSHao Wu {
719c8283b0fSHao Wu NPCM7xxClockDividerState *div = NPCM7XX_CLOCK_DIVIDER(obj);
720c8283b0fSHao Wu
721c8283b0fSHao Wu div->clock_in = qdev_init_clock_in(DEVICE(div), "clock-in",
722c8283b0fSHao Wu npcm7xx_clk_update_divider_cb,
723c8283b0fSHao Wu div, ClockUpdate);
724c8283b0fSHao Wu div->clock_out = qdev_init_clock_out(DEVICE(div), "clock-out");
725c8283b0fSHao Wu }
726c8283b0fSHao Wu
npcm7xx_init_clock_pll(NPCM7xxClockPLLState * pll,NPCMCLKState * clk,const PLLInitInfo * init_info)727c8283b0fSHao Wu static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState *pll,
728ca6d6a94SHao Wu NPCMCLKState *clk, const PLLInitInfo *init_info)
729c8283b0fSHao Wu {
730c8283b0fSHao Wu pll->name = init_info->name;
731c8283b0fSHao Wu pll->clk = clk;
732c8283b0fSHao Wu pll->reg = init_info->reg;
733c8283b0fSHao Wu if (init_info->public_name != NULL) {
734c8283b0fSHao Wu qdev_alias_clock(DEVICE(pll), "clock-out", DEVICE(clk),
735c8283b0fSHao Wu init_info->public_name);
736c8283b0fSHao Wu }
737c8283b0fSHao Wu }
738c8283b0fSHao Wu
npcm7xx_init_clock_sel(NPCM7xxClockSELState * sel,NPCMCLKState * clk,const SELInitInfo * init_info)739c8283b0fSHao Wu static void npcm7xx_init_clock_sel(NPCM7xxClockSELState *sel,
740ca6d6a94SHao Wu NPCMCLKState *clk, const SELInitInfo *init_info)
741c8283b0fSHao Wu {
742c8283b0fSHao Wu int input_size = init_info->input_size;
743c8283b0fSHao Wu
744c8283b0fSHao Wu sel->name = init_info->name;
745c8283b0fSHao Wu sel->clk = clk;
746c8283b0fSHao Wu sel->input_size = init_info->input_size;
747c8283b0fSHao Wu g_assert(input_size <= NPCM7XX_CLK_SEL_MAX_INPUT);
748c8283b0fSHao Wu sel->offset = init_info->offset;
749c8283b0fSHao Wu sel->len = init_info->len;
750c8283b0fSHao Wu if (init_info->public_name != NULL) {
751c8283b0fSHao Wu qdev_alias_clock(DEVICE(sel), "clock-out", DEVICE(clk),
752c8283b0fSHao Wu init_info->public_name);
753c8283b0fSHao Wu }
754c8283b0fSHao Wu }
755c8283b0fSHao Wu
npcm7xx_init_clock_divider(NPCM7xxClockDividerState * div,NPCMCLKState * clk,const DividerInitInfo * init_info)756c8283b0fSHao Wu static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState *div,
757ca6d6a94SHao Wu NPCMCLKState *clk, const DividerInitInfo *init_info)
758c8283b0fSHao Wu {
759c8283b0fSHao Wu div->name = init_info->name;
760c8283b0fSHao Wu div->clk = clk;
761c8283b0fSHao Wu
762c8283b0fSHao Wu div->divide = init_info->divide;
763c8283b0fSHao Wu if (div->divide == divide_by_constant) {
764c8283b0fSHao Wu div->divisor = init_info->divisor;
765c8283b0fSHao Wu } else {
766c8283b0fSHao Wu div->reg = init_info->reg;
767c8283b0fSHao Wu div->offset = init_info->offset;
768c8283b0fSHao Wu div->len = init_info->len;
769c8283b0fSHao Wu }
770c8283b0fSHao Wu if (init_info->public_name != NULL) {
771c8283b0fSHao Wu qdev_alias_clock(DEVICE(div), "clock-out", DEVICE(clk),
772c8283b0fSHao Wu init_info->public_name);
773c8283b0fSHao Wu }
774c8283b0fSHao Wu }
775c8283b0fSHao Wu
npcm7xx_get_clock(NPCMCLKState * clk,ClockSrcType type,int index)776ca6d6a94SHao Wu static Clock *npcm7xx_get_clock(NPCMCLKState *clk, ClockSrcType type,
777c8283b0fSHao Wu int index)
778c8283b0fSHao Wu {
779c8283b0fSHao Wu switch (type) {
780c8283b0fSHao Wu case CLKSRC_REF:
781c8283b0fSHao Wu return clk->clkref;
782c8283b0fSHao Wu case CLKSRC_PLL:
783c8283b0fSHao Wu return clk->plls[index].clock_out;
784c8283b0fSHao Wu case CLKSRC_SEL:
785c8283b0fSHao Wu return clk->sels[index].clock_out;
786c8283b0fSHao Wu case CLKSRC_DIV:
787c8283b0fSHao Wu return clk->dividers[index].clock_out;
788c8283b0fSHao Wu default:
789c8283b0fSHao Wu g_assert_not_reached();
790c8283b0fSHao Wu }
791c8283b0fSHao Wu }
792c8283b0fSHao Wu
npcm7xx_connect_clocks(NPCMCLKState * clk)793ca6d6a94SHao Wu static void npcm7xx_connect_clocks(NPCMCLKState *clk)
794c8283b0fSHao Wu {
795c8283b0fSHao Wu int i, j;
796c8283b0fSHao Wu Clock *src;
797c8283b0fSHao Wu
798c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) {
799c8283b0fSHao Wu src = npcm7xx_get_clock(clk, pll_init_info_list[i].src_type,
800c8283b0fSHao Wu pll_init_info_list[i].src_index);
801c8283b0fSHao Wu clock_set_source(clk->plls[i].clock_in, src);
802c8283b0fSHao Wu }
803c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) {
804c8283b0fSHao Wu for (j = 0; j < sel_init_info_list[i].input_size; ++j) {
805c8283b0fSHao Wu src = npcm7xx_get_clock(clk, sel_init_info_list[i].src_type[j],
806c8283b0fSHao Wu sel_init_info_list[i].src_index[j]);
807c8283b0fSHao Wu clock_set_source(clk->sels[i].clock_in[j], src);
808c8283b0fSHao Wu }
809c8283b0fSHao Wu }
810c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) {
811c8283b0fSHao Wu src = npcm7xx_get_clock(clk, divider_init_info_list[i].src_type,
812c8283b0fSHao Wu divider_init_info_list[i].src_index);
813c8283b0fSHao Wu clock_set_source(clk->dividers[i].clock_in, src);
814c8283b0fSHao Wu }
815c8283b0fSHao Wu }
816c8283b0fSHao Wu
npcm_clk_read(void * opaque,hwaddr offset,unsigned size)817ca6d6a94SHao Wu static uint64_t npcm_clk_read(void *opaque, hwaddr offset, unsigned size)
818c8283b0fSHao Wu {
819c8283b0fSHao Wu uint32_t reg = offset / sizeof(uint32_t);
820ca6d6a94SHao Wu NPCMCLKState *s = opaque;
821cf76c4e1SHao Wu NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s);
822c8283b0fSHao Wu int64_t now_ns;
823c8283b0fSHao Wu uint32_t value = 0;
824c8283b0fSHao Wu
825cf76c4e1SHao Wu if (reg >= c->nr_regs) {
826c8283b0fSHao Wu qemu_log_mask(LOG_GUEST_ERROR,
827c8283b0fSHao Wu "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
828c8283b0fSHao Wu __func__, offset);
829c8283b0fSHao Wu return 0;
830c8283b0fSHao Wu }
831c8283b0fSHao Wu
832c8283b0fSHao Wu switch (reg) {
833c8283b0fSHao Wu case NPCM7XX_CLK_SWRSTR:
834c8283b0fSHao Wu qemu_log_mask(LOG_GUEST_ERROR,
835c8283b0fSHao Wu "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n",
836c8283b0fSHao Wu __func__, offset);
837c8283b0fSHao Wu break;
838c8283b0fSHao Wu
839c8283b0fSHao Wu case NPCM7XX_CLK_SECCNT:
840c8283b0fSHao Wu now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
841c8283b0fSHao Wu value = (now_ns - s->ref_ns) / NANOSECONDS_PER_SECOND;
842c8283b0fSHao Wu break;
843c8283b0fSHao Wu
844c8283b0fSHao Wu case NPCM7XX_CLK_CNTR25M:
845c8283b0fSHao Wu now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
846c8283b0fSHao Wu /*
847c8283b0fSHao Wu * This register counts 25 MHz cycles, updating every 640 ns. It rolls
848c8283b0fSHao Wu * over to zero every second.
849c8283b0fSHao Wu *
850c8283b0fSHao Wu * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000.
851c8283b0fSHao Wu */
852c8283b0fSHao Wu value = (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_CLOCK_REF_HZ;
853c8283b0fSHao Wu break;
854c8283b0fSHao Wu
855c8283b0fSHao Wu default:
856c8283b0fSHao Wu value = s->regs[reg];
857c8283b0fSHao Wu break;
858c8283b0fSHao Wu };
859c8283b0fSHao Wu
860ca6d6a94SHao Wu trace_npcm_clk_read(offset, value);
861c8283b0fSHao Wu
862c8283b0fSHao Wu return value;
863c8283b0fSHao Wu }
864c8283b0fSHao Wu
npcm_clk_write(void * opaque,hwaddr offset,uint64_t v,unsigned size)865ca6d6a94SHao Wu static void npcm_clk_write(void *opaque, hwaddr offset,
866c8283b0fSHao Wu uint64_t v, unsigned size)
867c8283b0fSHao Wu {
868c8283b0fSHao Wu uint32_t reg = offset / sizeof(uint32_t);
869ca6d6a94SHao Wu NPCMCLKState *s = opaque;
870cf76c4e1SHao Wu NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s);
871c8283b0fSHao Wu uint32_t value = v;
872c8283b0fSHao Wu
873ca6d6a94SHao Wu trace_npcm_clk_write(offset, value);
874c8283b0fSHao Wu
875cf76c4e1SHao Wu if (reg >= c->nr_regs) {
876c8283b0fSHao Wu qemu_log_mask(LOG_GUEST_ERROR,
877c8283b0fSHao Wu "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
878c8283b0fSHao Wu __func__, offset);
879c8283b0fSHao Wu return;
880c8283b0fSHao Wu }
881c8283b0fSHao Wu
882c8283b0fSHao Wu switch (reg) {
883c8283b0fSHao Wu case NPCM7XX_CLK_SWRSTR:
884c8283b0fSHao Wu qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n",
885c8283b0fSHao Wu __func__, value);
886c8283b0fSHao Wu value = 0;
887c8283b0fSHao Wu break;
888c8283b0fSHao Wu
889c8283b0fSHao Wu case NPCM7XX_CLK_PLLCON0:
890c8283b0fSHao Wu case NPCM7XX_CLK_PLLCON1:
891c8283b0fSHao Wu case NPCM7XX_CLK_PLLCON2:
892c8283b0fSHao Wu case NPCM7XX_CLK_PLLCONG:
893c8283b0fSHao Wu if (value & PLLCON_PWDEN) {
894c8283b0fSHao Wu /* Power down -- clear lock and indicate loss of lock */
895c8283b0fSHao Wu value &= ~PLLCON_LOKI;
896c8283b0fSHao Wu value |= PLLCON_LOKS;
897c8283b0fSHao Wu } else {
898c8283b0fSHao Wu /* Normal mode -- assume always locked */
899c8283b0fSHao Wu value |= PLLCON_LOKI;
900c8283b0fSHao Wu /* Keep LOKS unchanged unless cleared by writing 1 */
901c8283b0fSHao Wu if (value & PLLCON_LOKS) {
902c8283b0fSHao Wu value &= ~PLLCON_LOKS;
903c8283b0fSHao Wu } else {
904c8283b0fSHao Wu value |= (value & PLLCON_LOKS);
905c8283b0fSHao Wu }
906c8283b0fSHao Wu }
907c8283b0fSHao Wu /* Only update PLL when it is locked. */
908c8283b0fSHao Wu if (value & PLLCON_LOKI) {
909c8283b0fSHao Wu npcm7xx_clk_update_pll(&s->plls[find_pll_by_reg(reg)]);
910c8283b0fSHao Wu }
911c8283b0fSHao Wu break;
912c8283b0fSHao Wu
913c8283b0fSHao Wu case NPCM7XX_CLK_CLKSEL:
914c8283b0fSHao Wu npcm7xx_clk_update_all_sels(s);
915c8283b0fSHao Wu break;
916c8283b0fSHao Wu
917c8283b0fSHao Wu case NPCM7XX_CLK_CLKDIV1:
918c8283b0fSHao Wu case NPCM7XX_CLK_CLKDIV2:
919c8283b0fSHao Wu case NPCM7XX_CLK_CLKDIV3:
920c8283b0fSHao Wu npcm7xx_clk_update_all_dividers(s);
921c8283b0fSHao Wu break;
922c8283b0fSHao Wu
923c8283b0fSHao Wu case NPCM7XX_CLK_CNTR25M:
924c8283b0fSHao Wu qemu_log_mask(LOG_GUEST_ERROR,
925c8283b0fSHao Wu "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
926c8283b0fSHao Wu __func__, offset);
927c8283b0fSHao Wu return;
928c8283b0fSHao Wu }
929c8283b0fSHao Wu
930c8283b0fSHao Wu s->regs[reg] = value;
931c8283b0fSHao Wu }
932c8283b0fSHao Wu
933c8283b0fSHao Wu /* Perform reset action triggered by a watchdog */
npcm7xx_clk_perform_watchdog_reset(void * opaque,int n,int level)934c8283b0fSHao Wu static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n,
935c8283b0fSHao Wu int level)
936c8283b0fSHao Wu {
937ca6d6a94SHao Wu NPCMCLKState *clk = NPCM_CLK(opaque);
938c8283b0fSHao Wu uint32_t rcr;
939c8283b0fSHao Wu
940c8283b0fSHao Wu g_assert(n >= 0 && n <= NPCM7XX_NR_WATCHDOGS);
941c8283b0fSHao Wu rcr = clk->regs[NPCM7XX_CLK_WD0RCR + n];
942c8283b0fSHao Wu if (rcr & NPCM7XX_CLK_WDRCR_CA9C) {
943c8283b0fSHao Wu watchdog_perform_action();
944c8283b0fSHao Wu } else {
945c8283b0fSHao Wu qemu_log_mask(LOG_UNIMP,
946c8283b0fSHao Wu "%s: only CPU reset is implemented. (requested 0x%" PRIx32")\n",
947c8283b0fSHao Wu __func__, rcr);
948c8283b0fSHao Wu }
949c8283b0fSHao Wu }
950c8283b0fSHao Wu
951ca6d6a94SHao Wu static const struct MemoryRegionOps npcm_clk_ops = {
952ca6d6a94SHao Wu .read = npcm_clk_read,
953ca6d6a94SHao Wu .write = npcm_clk_write,
954c8283b0fSHao Wu .endianness = DEVICE_LITTLE_ENDIAN,
955c8283b0fSHao Wu .valid = {
956c8283b0fSHao Wu .min_access_size = 4,
957c8283b0fSHao Wu .max_access_size = 4,
958c8283b0fSHao Wu .unaligned = false,
959c8283b0fSHao Wu },
960c8283b0fSHao Wu };
961c8283b0fSHao Wu
npcm_clk_enter_reset(Object * obj,ResetType type)962ca6d6a94SHao Wu static void npcm_clk_enter_reset(Object *obj, ResetType type)
963c8283b0fSHao Wu {
964ca6d6a94SHao Wu NPCMCLKState *s = NPCM_CLK(obj);
965cf76c4e1SHao Wu NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s);
966c8283b0fSHao Wu
9672cac20cbSPierrick Bouvier size_t sizeof_regs = c->nr_regs * sizeof(uint32_t);
9682cac20cbSPierrick Bouvier g_assert(sizeof(s->regs) >= sizeof_regs);
9692cac20cbSPierrick Bouvier memcpy(s->regs, c->cold_reset_values, sizeof_regs);
970c8283b0fSHao Wu s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
971c8283b0fSHao Wu npcm7xx_clk_update_all_clocks(s);
972c8283b0fSHao Wu /*
973c8283b0fSHao Wu * A small number of registers need to be reset on a core domain reset,
974c8283b0fSHao Wu * but no such reset type exists yet.
975c8283b0fSHao Wu */
976c8283b0fSHao Wu }
977c8283b0fSHao Wu
npcm7xx_clk_init_clock_hierarchy(NPCMCLKState * s)978ca6d6a94SHao Wu static void npcm7xx_clk_init_clock_hierarchy(NPCMCLKState *s)
979c8283b0fSHao Wu {
980c8283b0fSHao Wu int i;
981c8283b0fSHao Wu
982c8283b0fSHao Wu s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL, 0);
983c8283b0fSHao Wu
984c8283b0fSHao Wu /* First pass: init all converter modules */
985c8283b0fSHao Wu QEMU_BUILD_BUG_ON(ARRAY_SIZE(pll_init_info_list) != NPCM7XX_CLOCK_NR_PLLS);
986c8283b0fSHao Wu QEMU_BUILD_BUG_ON(ARRAY_SIZE(sel_init_info_list) != NPCM7XX_CLOCK_NR_SELS);
987c8283b0fSHao Wu QEMU_BUILD_BUG_ON(ARRAY_SIZE(divider_init_info_list)
988c8283b0fSHao Wu != NPCM7XX_CLOCK_NR_DIVIDERS);
989c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) {
990c8283b0fSHao Wu object_initialize_child(OBJECT(s), pll_init_info_list[i].name,
991c8283b0fSHao Wu &s->plls[i], TYPE_NPCM7XX_CLOCK_PLL);
992c8283b0fSHao Wu npcm7xx_init_clock_pll(&s->plls[i], s,
993c8283b0fSHao Wu &pll_init_info_list[i]);
994c8283b0fSHao Wu }
995c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) {
996c8283b0fSHao Wu object_initialize_child(OBJECT(s), sel_init_info_list[i].name,
997c8283b0fSHao Wu &s->sels[i], TYPE_NPCM7XX_CLOCK_SEL);
998c8283b0fSHao Wu npcm7xx_init_clock_sel(&s->sels[i], s,
999c8283b0fSHao Wu &sel_init_info_list[i]);
1000c8283b0fSHao Wu }
1001c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) {
1002c8283b0fSHao Wu object_initialize_child(OBJECT(s), divider_init_info_list[i].name,
1003c8283b0fSHao Wu &s->dividers[i], TYPE_NPCM7XX_CLOCK_DIVIDER);
1004c8283b0fSHao Wu npcm7xx_init_clock_divider(&s->dividers[i], s,
1005c8283b0fSHao Wu ÷r_init_info_list[i]);
1006c8283b0fSHao Wu }
1007c8283b0fSHao Wu
1008c8283b0fSHao Wu /* Second pass: connect converter modules */
1009c8283b0fSHao Wu npcm7xx_connect_clocks(s);
1010c8283b0fSHao Wu
1011c8283b0fSHao Wu clock_update_hz(s->clkref, NPCM7XX_CLOCK_REF_HZ);
1012c8283b0fSHao Wu }
1013c8283b0fSHao Wu
npcm_clk_init(Object * obj)1014ca6d6a94SHao Wu static void npcm_clk_init(Object *obj)
1015c8283b0fSHao Wu {
1016ca6d6a94SHao Wu NPCMCLKState *s = NPCM_CLK(obj);
1017c8283b0fSHao Wu
1018ca6d6a94SHao Wu memory_region_init_io(&s->iomem, obj, &npcm_clk_ops, s,
1019ca6d6a94SHao Wu TYPE_NPCM_CLK, 4 * KiB);
1020c8283b0fSHao Wu sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
1021c8283b0fSHao Wu }
1022c8283b0fSHao Wu
npcm_clk_post_load(void * opaque,int version_id)1023ca6d6a94SHao Wu static int npcm_clk_post_load(void *opaque, int version_id)
1024c8283b0fSHao Wu {
1025c8283b0fSHao Wu if (version_id >= 1) {
1026ca6d6a94SHao Wu NPCMCLKState *clk = opaque;
1027c8283b0fSHao Wu
1028c8283b0fSHao Wu npcm7xx_clk_update_all_clocks(clk);
1029c8283b0fSHao Wu }
1030c8283b0fSHao Wu
1031c8283b0fSHao Wu return 0;
1032c8283b0fSHao Wu }
1033c8283b0fSHao Wu
npcm_clk_realize(DeviceState * dev,Error ** errp)1034ca6d6a94SHao Wu static void npcm_clk_realize(DeviceState *dev, Error **errp)
1035c8283b0fSHao Wu {
1036c8283b0fSHao Wu int i;
1037ca6d6a94SHao Wu NPCMCLKState *s = NPCM_CLK(dev);
1038c8283b0fSHao Wu
1039c8283b0fSHao Wu qdev_init_gpio_in_named(DEVICE(s), npcm7xx_clk_perform_watchdog_reset,
1040c8283b0fSHao Wu NPCM7XX_WATCHDOG_RESET_GPIO_IN, NPCM7XX_NR_WATCHDOGS);
1041c8283b0fSHao Wu npcm7xx_clk_init_clock_hierarchy(s);
1042c8283b0fSHao Wu
1043c8283b0fSHao Wu /* Realize child devices */
1044c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) {
1045c8283b0fSHao Wu if (!qdev_realize(DEVICE(&s->plls[i]), NULL, errp)) {
1046c8283b0fSHao Wu return;
1047c8283b0fSHao Wu }
1048c8283b0fSHao Wu }
1049c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) {
1050c8283b0fSHao Wu if (!qdev_realize(DEVICE(&s->sels[i]), NULL, errp)) {
1051c8283b0fSHao Wu return;
1052c8283b0fSHao Wu }
1053c8283b0fSHao Wu }
1054c8283b0fSHao Wu for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) {
1055c8283b0fSHao Wu if (!qdev_realize(DEVICE(&s->dividers[i]), NULL, errp)) {
1056c8283b0fSHao Wu return;
1057c8283b0fSHao Wu }
1058c8283b0fSHao Wu }
1059c8283b0fSHao Wu }
1060c8283b0fSHao Wu
1061c8283b0fSHao Wu static const VMStateDescription vmstate_npcm7xx_clk_pll = {
1062c8283b0fSHao Wu .name = "npcm7xx-clock-pll",
1063c8283b0fSHao Wu .version_id = 0,
1064c8283b0fSHao Wu .minimum_version_id = 0,
1065c8283b0fSHao Wu .fields = (const VMStateField[]) {
1066c8283b0fSHao Wu VMSTATE_CLOCK(clock_in, NPCM7xxClockPLLState),
1067c8283b0fSHao Wu VMSTATE_END_OF_LIST(),
1068c8283b0fSHao Wu },
1069c8283b0fSHao Wu };
1070c8283b0fSHao Wu
1071c8283b0fSHao Wu static const VMStateDescription vmstate_npcm7xx_clk_sel = {
1072c8283b0fSHao Wu .name = "npcm7xx-clock-sel",
1073c8283b0fSHao Wu .version_id = 0,
1074c8283b0fSHao Wu .minimum_version_id = 0,
1075c8283b0fSHao Wu .fields = (const VMStateField[]) {
1076c8283b0fSHao Wu VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(clock_in, NPCM7xxClockSELState,
1077c8283b0fSHao Wu NPCM7XX_CLK_SEL_MAX_INPUT, 0, vmstate_clock, Clock),
1078c8283b0fSHao Wu VMSTATE_END_OF_LIST(),
1079c8283b0fSHao Wu },
1080c8283b0fSHao Wu };
1081c8283b0fSHao Wu
1082c8283b0fSHao Wu static const VMStateDescription vmstate_npcm7xx_clk_divider = {
1083c8283b0fSHao Wu .name = "npcm7xx-clock-divider",
1084c8283b0fSHao Wu .version_id = 0,
1085c8283b0fSHao Wu .minimum_version_id = 0,
1086c8283b0fSHao Wu .fields = (const VMStateField[]) {
1087c8283b0fSHao Wu VMSTATE_CLOCK(clock_in, NPCM7xxClockDividerState),
1088c8283b0fSHao Wu VMSTATE_END_OF_LIST(),
1089c8283b0fSHao Wu },
1090c8283b0fSHao Wu };
1091c8283b0fSHao Wu
1092ca6d6a94SHao Wu static const VMStateDescription vmstate_npcm_clk = {
1093ca6d6a94SHao Wu .name = "npcm-clk",
10944e67d50dSHao Wu .version_id = 3,
10954e67d50dSHao Wu .minimum_version_id = 3,
1096ca6d6a94SHao Wu .post_load = npcm_clk_post_load,
1097c8283b0fSHao Wu .fields = (const VMStateField[]) {
1098ca6d6a94SHao Wu VMSTATE_UINT32_ARRAY(regs, NPCMCLKState, NPCM_CLK_MAX_NR_REGS),
1099ca6d6a94SHao Wu VMSTATE_INT64(ref_ns, NPCMCLKState),
1100ca6d6a94SHao Wu VMSTATE_CLOCK(clkref, NPCMCLKState),
1101c8283b0fSHao Wu VMSTATE_END_OF_LIST(),
1102c8283b0fSHao Wu },
1103c8283b0fSHao Wu };
1104c8283b0fSHao Wu
npcm7xx_clk_pll_class_init(ObjectClass * klass,void * data)1105c8283b0fSHao Wu static void npcm7xx_clk_pll_class_init(ObjectClass *klass, void *data)
1106c8283b0fSHao Wu {
1107c8283b0fSHao Wu DeviceClass *dc = DEVICE_CLASS(klass);
1108c8283b0fSHao Wu
1109c8283b0fSHao Wu dc->desc = "NPCM7xx Clock PLL Module";
1110c8283b0fSHao Wu dc->vmsd = &vmstate_npcm7xx_clk_pll;
1111*490aaae9SPhilippe Mathieu-Daudé /* Reason: Part of NPCMCLKState component */
1112*490aaae9SPhilippe Mathieu-Daudé dc->user_creatable = false;
1113c8283b0fSHao Wu }
1114c8283b0fSHao Wu
npcm7xx_clk_sel_class_init(ObjectClass * klass,void * data)1115c8283b0fSHao Wu static void npcm7xx_clk_sel_class_init(ObjectClass *klass, void *data)
1116c8283b0fSHao Wu {
1117c8283b0fSHao Wu DeviceClass *dc = DEVICE_CLASS(klass);
1118c8283b0fSHao Wu
1119c8283b0fSHao Wu dc->desc = "NPCM7xx Clock SEL Module";
1120c8283b0fSHao Wu dc->vmsd = &vmstate_npcm7xx_clk_sel;
1121*490aaae9SPhilippe Mathieu-Daudé /* Reason: Part of NPCMCLKState component */
1122*490aaae9SPhilippe Mathieu-Daudé dc->user_creatable = false;
1123c8283b0fSHao Wu }
1124c8283b0fSHao Wu
npcm7xx_clk_divider_class_init(ObjectClass * klass,void * data)1125c8283b0fSHao Wu static void npcm7xx_clk_divider_class_init(ObjectClass *klass, void *data)
1126c8283b0fSHao Wu {
1127c8283b0fSHao Wu DeviceClass *dc = DEVICE_CLASS(klass);
1128c8283b0fSHao Wu
1129c8283b0fSHao Wu dc->desc = "NPCM7xx Clock Divider Module";
1130c8283b0fSHao Wu dc->vmsd = &vmstate_npcm7xx_clk_divider;
1131*490aaae9SPhilippe Mathieu-Daudé /* Reason: Part of NPCMCLKState component */
1132*490aaae9SPhilippe Mathieu-Daudé dc->user_creatable = false;
1133c8283b0fSHao Wu }
1134c8283b0fSHao Wu
npcm_clk_class_init(ObjectClass * klass,void * data)1135ca6d6a94SHao Wu static void npcm_clk_class_init(ObjectClass *klass, void *data)
1136c8283b0fSHao Wu {
1137c8283b0fSHao Wu ResettableClass *rc = RESETTABLE_CLASS(klass);
1138c8283b0fSHao Wu DeviceClass *dc = DEVICE_CLASS(klass);
1139c8283b0fSHao Wu
1140ca6d6a94SHao Wu dc->vmsd = &vmstate_npcm_clk;
1141ca6d6a94SHao Wu dc->realize = npcm_clk_realize;
1142ca6d6a94SHao Wu rc->phases.enter = npcm_clk_enter_reset;
1143ca6d6a94SHao Wu }
1144c8283b0fSHao Wu
npcm7xx_clk_class_init(ObjectClass * klass,void * data)1145ca6d6a94SHao Wu static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
1146ca6d6a94SHao Wu {
1147cf76c4e1SHao Wu NPCMCLKClass *c = NPCM_CLK_CLASS(klass);
1148ca6d6a94SHao Wu DeviceClass *dc = DEVICE_CLASS(klass);
1149ca6d6a94SHao Wu
1150c8283b0fSHao Wu dc->desc = "NPCM7xx Clock Control Registers";
1151cf76c4e1SHao Wu c->nr_regs = NPCM7XX_CLK_NR_REGS;
1152cf76c4e1SHao Wu c->cold_reset_values = npcm7xx_cold_reset_values;
1153c8283b0fSHao Wu }
1154c8283b0fSHao Wu
npcm8xx_clk_class_init(ObjectClass * klass,void * data)11554e67d50dSHao Wu static void npcm8xx_clk_class_init(ObjectClass *klass, void *data)
11564e67d50dSHao Wu {
11574e67d50dSHao Wu NPCMCLKClass *c = NPCM_CLK_CLASS(klass);
11584e67d50dSHao Wu DeviceClass *dc = DEVICE_CLASS(klass);
11594e67d50dSHao Wu
11604e67d50dSHao Wu dc->desc = "NPCM8xx Clock Control Registers";
11614e67d50dSHao Wu c->nr_regs = NPCM8XX_CLK_NR_REGS;
11624e67d50dSHao Wu c->cold_reset_values = npcm8xx_cold_reset_values;
11634e67d50dSHao Wu }
11644e67d50dSHao Wu
1165c8283b0fSHao Wu static const TypeInfo npcm7xx_clk_pll_info = {
1166c8283b0fSHao Wu .name = TYPE_NPCM7XX_CLOCK_PLL,
1167c8283b0fSHao Wu .parent = TYPE_DEVICE,
1168c8283b0fSHao Wu .instance_size = sizeof(NPCM7xxClockPLLState),
1169c8283b0fSHao Wu .instance_init = npcm7xx_clk_pll_init,
1170c8283b0fSHao Wu .class_init = npcm7xx_clk_pll_class_init,
1171c8283b0fSHao Wu };
1172c8283b0fSHao Wu
1173c8283b0fSHao Wu static const TypeInfo npcm7xx_clk_sel_info = {
1174c8283b0fSHao Wu .name = TYPE_NPCM7XX_CLOCK_SEL,
1175c8283b0fSHao Wu .parent = TYPE_DEVICE,
1176c8283b0fSHao Wu .instance_size = sizeof(NPCM7xxClockSELState),
1177c8283b0fSHao Wu .instance_init = npcm7xx_clk_sel_init,
1178c8283b0fSHao Wu .class_init = npcm7xx_clk_sel_class_init,
1179c8283b0fSHao Wu };
1180c8283b0fSHao Wu
1181c8283b0fSHao Wu static const TypeInfo npcm7xx_clk_divider_info = {
1182c8283b0fSHao Wu .name = TYPE_NPCM7XX_CLOCK_DIVIDER,
1183c8283b0fSHao Wu .parent = TYPE_DEVICE,
1184c8283b0fSHao Wu .instance_size = sizeof(NPCM7xxClockDividerState),
1185c8283b0fSHao Wu .instance_init = npcm7xx_clk_divider_init,
1186c8283b0fSHao Wu .class_init = npcm7xx_clk_divider_class_init,
1187c8283b0fSHao Wu };
1188c8283b0fSHao Wu
1189ca6d6a94SHao Wu static const TypeInfo npcm_clk_info = {
1190ca6d6a94SHao Wu .name = TYPE_NPCM_CLK,
1191ca6d6a94SHao Wu .parent = TYPE_SYS_BUS_DEVICE,
1192ca6d6a94SHao Wu .instance_size = sizeof(NPCMCLKState),
1193ca6d6a94SHao Wu .instance_init = npcm_clk_init,
1194cf76c4e1SHao Wu .class_size = sizeof(NPCMCLKClass),
1195ca6d6a94SHao Wu .class_init = npcm_clk_class_init,
1196ca6d6a94SHao Wu .abstract = true,
1197ca6d6a94SHao Wu };
1198ca6d6a94SHao Wu
1199c8283b0fSHao Wu static const TypeInfo npcm7xx_clk_info = {
1200c8283b0fSHao Wu .name = TYPE_NPCM7XX_CLK,
1201ca6d6a94SHao Wu .parent = TYPE_NPCM_CLK,
1202c8283b0fSHao Wu .class_init = npcm7xx_clk_class_init,
1203c8283b0fSHao Wu };
1204c8283b0fSHao Wu
12054e67d50dSHao Wu static const TypeInfo npcm8xx_clk_info = {
12064e67d50dSHao Wu .name = TYPE_NPCM8XX_CLK,
12074e67d50dSHao Wu .parent = TYPE_NPCM_CLK,
12084e67d50dSHao Wu .class_init = npcm8xx_clk_class_init,
12094e67d50dSHao Wu };
12104e67d50dSHao Wu
npcm7xx_clk_register_type(void)1211c8283b0fSHao Wu static void npcm7xx_clk_register_type(void)
1212c8283b0fSHao Wu {
1213c8283b0fSHao Wu type_register_static(&npcm7xx_clk_pll_info);
1214c8283b0fSHao Wu type_register_static(&npcm7xx_clk_sel_info);
1215c8283b0fSHao Wu type_register_static(&npcm7xx_clk_divider_info);
1216ca6d6a94SHao Wu type_register_static(&npcm_clk_info);
1217c8283b0fSHao Wu type_register_static(&npcm7xx_clk_info);
12184e67d50dSHao Wu type_register_static(&npcm8xx_clk_info);
1219c8283b0fSHao Wu }
1220c8283b0fSHao Wu type_init(npcm7xx_clk_register_type);
1221