xref: /qemu/hw/misc/bcm2835_cprman.c (revision 09d56bbc9bc2f40865764b06b9830a9504bd3f9a)
1fc14176bSLuc Michel /*
2fc14176bSLuc Michel  * BCM2835 CPRMAN clock manager
3fc14176bSLuc Michel  *
4fc14176bSLuc Michel  * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
5fc14176bSLuc Michel  *
6fc14176bSLuc Michel  * SPDX-License-Identifier: GPL-2.0-or-later
7fc14176bSLuc Michel  */
8fc14176bSLuc Michel 
9fc14176bSLuc Michel /*
10fc14176bSLuc Michel  * This peripheral is roughly divided into 3 main parts:
11fc14176bSLuc Michel  *   - the PLLs
12fc14176bSLuc Michel  *   - the PLL channels
13fc14176bSLuc Michel  *   - the clock muxes
14fc14176bSLuc Michel  *
15fc14176bSLuc Michel  * A main oscillator (xosc) feeds all the PLLs. Each PLLs has one or more
16fc14176bSLuc Michel  * channels. Those channel are then connected to the clock muxes. Each mux has
17fc14176bSLuc Michel  * multiples sources (usually the xosc, some of the PLL channels and some "test
18fc14176bSLuc Michel  * debug" clocks). A mux is configured to select a given source through its
19fc14176bSLuc Michel  * control register. Each mux has one output clock that also goes out of the
20fc14176bSLuc Michel  * CPRMAN. This output clock usually connects to another peripheral in the SoC
21fc14176bSLuc Michel  * (so a given mux is dedicated to a peripheral).
22fc14176bSLuc Michel  *
23fc14176bSLuc Michel  * At each level (PLL, channel and mux), the clock can be altered through
24fc14176bSLuc Michel  * dividers (and multipliers in case of the PLLs), and can be disabled (in this
25fc14176bSLuc Michel  * case, the next levels see no clock).
26fc14176bSLuc Michel  *
27fc14176bSLuc Michel  * This can be sum-up as follows (this is an example and not the actual BCM2835
28fc14176bSLuc Michel  * clock tree):
29fc14176bSLuc Michel  *
30fc14176bSLuc Michel  *          /-->[PLL]-|->[PLL channel]--...            [mux]--> to peripherals
31fc14176bSLuc Michel  *          |         |->[PLL channel]  muxes takes    [mux]
32fc14176bSLuc Michel  *          |         \->[PLL channel]  inputs from    [mux]
33fc14176bSLuc Michel  *          |                           some channels  [mux]
34fc14176bSLuc Michel  * [xosc]---|-->[PLL]-|->[PLL channel]  and other srcs [mux]
35fc14176bSLuc Michel  *          |         \->[PLL channel]           ...-->[mux]
36fc14176bSLuc Michel  *          |                                          [mux]
37fc14176bSLuc Michel  *          \-->[PLL]--->[PLL channel]                 [mux]
38fc14176bSLuc Michel  *
39fc14176bSLuc Michel  * The page at https://elinux.org/The_Undocumented_Pi gives the actual clock
40fc14176bSLuc Michel  * tree configuration.
41fc14176bSLuc Michel  */
42fc14176bSLuc Michel 
43fc14176bSLuc Michel #include "qemu/osdep.h"
44fc14176bSLuc Michel #include "qemu/log.h"
45fc14176bSLuc Michel #include "migration/vmstate.h"
46fc14176bSLuc Michel #include "hw/qdev-properties.h"
47fc14176bSLuc Michel #include "hw/misc/bcm2835_cprman.h"
48fc14176bSLuc Michel #include "hw/misc/bcm2835_cprman_internals.h"
49fc14176bSLuc Michel #include "trace.h"
50fc14176bSLuc Michel 
511e986e25SLuc Michel /* PLL */
521e986e25SLuc Michel 
536d2b874cSLuc Michel static bool pll_is_locked(const CprmanPllState *pll)
546d2b874cSLuc Michel {
556d2b874cSLuc Michel     return !FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PWRDN)
566d2b874cSLuc Michel         && !FIELD_EX32(*pll->reg_cm, CM_PLLx, ANARST);
576d2b874cSLuc Michel }
586d2b874cSLuc Michel 
591e986e25SLuc Michel static void pll_update(CprmanPllState *pll)
601e986e25SLuc Michel {
616d2b874cSLuc Michel     uint64_t freq, ndiv, fdiv, pdiv;
626d2b874cSLuc Michel 
636d2b874cSLuc Michel     if (!pll_is_locked(pll)) {
641e986e25SLuc Michel         clock_update(pll->out, 0);
656d2b874cSLuc Michel         return;
666d2b874cSLuc Michel     }
676d2b874cSLuc Michel 
686d2b874cSLuc Michel     pdiv = FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PDIV);
696d2b874cSLuc Michel 
706d2b874cSLuc Michel     if (!pdiv) {
716d2b874cSLuc Michel         clock_update(pll->out, 0);
726d2b874cSLuc Michel         return;
736d2b874cSLuc Michel     }
746d2b874cSLuc Michel 
756d2b874cSLuc Michel     ndiv = FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, NDIV);
766d2b874cSLuc Michel     fdiv = FIELD_EX32(*pll->reg_a2w_frac, A2W_PLLx_FRAC, FRAC);
776d2b874cSLuc Michel 
786d2b874cSLuc Michel     if (pll->reg_a2w_ana[1] & pll->prediv_mask) {
796d2b874cSLuc Michel         /* The prescaler doubles the parent frequency */
806d2b874cSLuc Michel         ndiv *= 2;
816d2b874cSLuc Michel         fdiv *= 2;
826d2b874cSLuc Michel     }
836d2b874cSLuc Michel 
846d2b874cSLuc Michel     /*
856d2b874cSLuc Michel      * We have a multiplier with an integer part (ndiv) and a fractional part
866d2b874cSLuc Michel      * (fdiv), and a divider (pdiv).
876d2b874cSLuc Michel      */
886d2b874cSLuc Michel     freq = clock_get_hz(pll->xosc_in) *
896d2b874cSLuc Michel         ((ndiv << R_A2W_PLLx_FRAC_FRAC_LENGTH) + fdiv);
906d2b874cSLuc Michel     freq /= pdiv;
916d2b874cSLuc Michel     freq >>= R_A2W_PLLx_FRAC_FRAC_LENGTH;
926d2b874cSLuc Michel 
936d2b874cSLuc Michel     clock_update_hz(pll->out, freq);
941e986e25SLuc Michel }
951e986e25SLuc Michel 
961e986e25SLuc Michel static void pll_xosc_update(void *opaque)
971e986e25SLuc Michel {
981e986e25SLuc Michel     pll_update(CPRMAN_PLL(opaque));
991e986e25SLuc Michel }
1001e986e25SLuc Michel 
1011e986e25SLuc Michel static void pll_init(Object *obj)
1021e986e25SLuc Michel {
1031e986e25SLuc Michel     CprmanPllState *s = CPRMAN_PLL(obj);
1041e986e25SLuc Michel 
1051e986e25SLuc Michel     s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update, s);
1061e986e25SLuc Michel     s->out = qdev_init_clock_out(DEVICE(s), "out");
1071e986e25SLuc Michel }
1081e986e25SLuc Michel 
1091e986e25SLuc Michel static const VMStateDescription pll_vmstate = {
1101e986e25SLuc Michel     .name = TYPE_CPRMAN_PLL,
1111e986e25SLuc Michel     .version_id = 1,
1121e986e25SLuc Michel     .minimum_version_id = 1,
1131e986e25SLuc Michel     .fields = (VMStateField[]) {
1141e986e25SLuc Michel         VMSTATE_CLOCK(xosc_in, CprmanPllState),
1151e986e25SLuc Michel         VMSTATE_END_OF_LIST()
1161e986e25SLuc Michel     }
1171e986e25SLuc Michel };
1181e986e25SLuc Michel 
1191e986e25SLuc Michel static void pll_class_init(ObjectClass *klass, void *data)
1201e986e25SLuc Michel {
1211e986e25SLuc Michel     DeviceClass *dc = DEVICE_CLASS(klass);
1221e986e25SLuc Michel 
1231e986e25SLuc Michel     dc->vmsd = &pll_vmstate;
1241e986e25SLuc Michel }
1251e986e25SLuc Michel 
1261e986e25SLuc Michel static const TypeInfo cprman_pll_info = {
1271e986e25SLuc Michel     .name = TYPE_CPRMAN_PLL,
1281e986e25SLuc Michel     .parent = TYPE_DEVICE,
1291e986e25SLuc Michel     .instance_size = sizeof(CprmanPllState),
1301e986e25SLuc Michel     .class_init = pll_class_init,
1311e986e25SLuc Michel     .instance_init = pll_init,
1321e986e25SLuc Michel };
1331e986e25SLuc Michel 
1341e986e25SLuc Michel 
135*09d56bbcSLuc Michel /* PLL channel */
136*09d56bbcSLuc Michel 
137*09d56bbcSLuc Michel static void pll_channel_update(CprmanPllChannelState *channel)
138*09d56bbcSLuc Michel {
139*09d56bbcSLuc Michel     clock_update(channel->out, 0);
140*09d56bbcSLuc Michel }
141*09d56bbcSLuc Michel 
142*09d56bbcSLuc Michel /* Update a PLL and all its channels */
143*09d56bbcSLuc Michel static void pll_update_all_channels(BCM2835CprmanState *s,
144*09d56bbcSLuc Michel                                     CprmanPllState *pll)
145*09d56bbcSLuc Michel {
146*09d56bbcSLuc Michel     size_t i;
147*09d56bbcSLuc Michel 
148*09d56bbcSLuc Michel     pll_update(pll);
149*09d56bbcSLuc Michel 
150*09d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
151*09d56bbcSLuc Michel         CprmanPllChannelState *channel = &s->channels[i];
152*09d56bbcSLuc Michel         if (channel->parent == pll->id) {
153*09d56bbcSLuc Michel             pll_channel_update(channel);
154*09d56bbcSLuc Michel         }
155*09d56bbcSLuc Michel     }
156*09d56bbcSLuc Michel }
157*09d56bbcSLuc Michel 
158*09d56bbcSLuc Michel static void pll_channel_pll_in_update(void *opaque)
159*09d56bbcSLuc Michel {
160*09d56bbcSLuc Michel     pll_channel_update(CPRMAN_PLL_CHANNEL(opaque));
161*09d56bbcSLuc Michel }
162*09d56bbcSLuc Michel 
163*09d56bbcSLuc Michel static void pll_channel_init(Object *obj)
164*09d56bbcSLuc Michel {
165*09d56bbcSLuc Michel     CprmanPllChannelState *s = CPRMAN_PLL_CHANNEL(obj);
166*09d56bbcSLuc Michel 
167*09d56bbcSLuc Michel     s->pll_in = qdev_init_clock_in(DEVICE(s), "pll-in",
168*09d56bbcSLuc Michel                                    pll_channel_pll_in_update, s);
169*09d56bbcSLuc Michel     s->out = qdev_init_clock_out(DEVICE(s), "out");
170*09d56bbcSLuc Michel }
171*09d56bbcSLuc Michel 
172*09d56bbcSLuc Michel static const VMStateDescription pll_channel_vmstate = {
173*09d56bbcSLuc Michel     .name = TYPE_CPRMAN_PLL_CHANNEL,
174*09d56bbcSLuc Michel     .version_id = 1,
175*09d56bbcSLuc Michel     .minimum_version_id = 1,
176*09d56bbcSLuc Michel     .fields = (VMStateField[]) {
177*09d56bbcSLuc Michel         VMSTATE_CLOCK(pll_in, CprmanPllChannelState),
178*09d56bbcSLuc Michel         VMSTATE_END_OF_LIST()
179*09d56bbcSLuc Michel     }
180*09d56bbcSLuc Michel };
181*09d56bbcSLuc Michel 
182*09d56bbcSLuc Michel static void pll_channel_class_init(ObjectClass *klass, void *data)
183*09d56bbcSLuc Michel {
184*09d56bbcSLuc Michel     DeviceClass *dc = DEVICE_CLASS(klass);
185*09d56bbcSLuc Michel 
186*09d56bbcSLuc Michel     dc->vmsd = &pll_channel_vmstate;
187*09d56bbcSLuc Michel }
188*09d56bbcSLuc Michel 
189*09d56bbcSLuc Michel static const TypeInfo cprman_pll_channel_info = {
190*09d56bbcSLuc Michel     .name = TYPE_CPRMAN_PLL_CHANNEL,
191*09d56bbcSLuc Michel     .parent = TYPE_DEVICE,
192*09d56bbcSLuc Michel     .instance_size = sizeof(CprmanPllChannelState),
193*09d56bbcSLuc Michel     .class_init = pll_channel_class_init,
194*09d56bbcSLuc Michel     .instance_init = pll_channel_init,
195*09d56bbcSLuc Michel };
196*09d56bbcSLuc Michel 
197*09d56bbcSLuc Michel 
198fc14176bSLuc Michel /* CPRMAN "top level" model */
199fc14176bSLuc Michel 
2006d2b874cSLuc Michel static uint32_t get_cm_lock(const BCM2835CprmanState *s)
2016d2b874cSLuc Michel {
2026d2b874cSLuc Michel     static const int CM_LOCK_MAPPING[CPRMAN_NUM_PLL] = {
2036d2b874cSLuc Michel         [CPRMAN_PLLA] = R_CM_LOCK_FLOCKA_SHIFT,
2046d2b874cSLuc Michel         [CPRMAN_PLLC] = R_CM_LOCK_FLOCKC_SHIFT,
2056d2b874cSLuc Michel         [CPRMAN_PLLD] = R_CM_LOCK_FLOCKD_SHIFT,
2066d2b874cSLuc Michel         [CPRMAN_PLLH] = R_CM_LOCK_FLOCKH_SHIFT,
2076d2b874cSLuc Michel         [CPRMAN_PLLB] = R_CM_LOCK_FLOCKB_SHIFT,
2086d2b874cSLuc Michel     };
2096d2b874cSLuc Michel 
2106d2b874cSLuc Michel     uint32_t r = 0;
2116d2b874cSLuc Michel     size_t i;
2126d2b874cSLuc Michel 
2136d2b874cSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
2146d2b874cSLuc Michel         r |= pll_is_locked(&s->plls[i]) << CM_LOCK_MAPPING[i];
2156d2b874cSLuc Michel     }
2166d2b874cSLuc Michel 
2176d2b874cSLuc Michel     return r;
2186d2b874cSLuc Michel }
2196d2b874cSLuc Michel 
220fc14176bSLuc Michel static uint64_t cprman_read(void *opaque, hwaddr offset,
221fc14176bSLuc Michel                             unsigned size)
222fc14176bSLuc Michel {
223fc14176bSLuc Michel     BCM2835CprmanState *s = CPRMAN(opaque);
224fc14176bSLuc Michel     uint64_t r = 0;
225fc14176bSLuc Michel     size_t idx = offset / sizeof(uint32_t);
226fc14176bSLuc Michel 
227fc14176bSLuc Michel     switch (idx) {
2286d2b874cSLuc Michel     case R_CM_LOCK:
2296d2b874cSLuc Michel         r = get_cm_lock(s);
2306d2b874cSLuc Michel         break;
2316d2b874cSLuc Michel 
232fc14176bSLuc Michel     default:
233fc14176bSLuc Michel         r = s->regs[idx];
234fc14176bSLuc Michel     }
235fc14176bSLuc Michel 
236fc14176bSLuc Michel     trace_bcm2835_cprman_read(offset, r);
237fc14176bSLuc Michel     return r;
238fc14176bSLuc Michel }
239fc14176bSLuc Michel 
240*09d56bbcSLuc Michel static inline void update_pll_and_channels_from_cm(BCM2835CprmanState *s,
241*09d56bbcSLuc Michel                                                    size_t idx)
242*09d56bbcSLuc Michel {
243*09d56bbcSLuc Michel     size_t i;
244*09d56bbcSLuc Michel 
245*09d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
246*09d56bbcSLuc Michel         if (PLL_INIT_INFO[i].cm_offset == idx) {
247*09d56bbcSLuc Michel             pll_update_all_channels(s, &s->plls[i]);
248*09d56bbcSLuc Michel             return;
249*09d56bbcSLuc Michel         }
250*09d56bbcSLuc Michel     }
251*09d56bbcSLuc Michel }
252*09d56bbcSLuc Michel 
253*09d56bbcSLuc Michel static inline void update_channel_from_a2w(BCM2835CprmanState *s, size_t idx)
254*09d56bbcSLuc Michel {
255*09d56bbcSLuc Michel     size_t i;
256*09d56bbcSLuc Michel 
257*09d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
258*09d56bbcSLuc Michel         if (PLL_CHANNEL_INIT_INFO[i].a2w_ctrl_offset == idx) {
259*09d56bbcSLuc Michel             pll_channel_update(&s->channels[i]);
260*09d56bbcSLuc Michel             return;
261*09d56bbcSLuc Michel         }
262*09d56bbcSLuc Michel     }
263*09d56bbcSLuc Michel }
264*09d56bbcSLuc Michel 
265*09d56bbcSLuc Michel #define CASE_PLL_A2W_REGS(pll_) \
2661e986e25SLuc Michel     case R_A2W_ ## pll_ ## _CTRL: \
2671e986e25SLuc Michel     case R_A2W_ ## pll_ ## _ANA0: \
2681e986e25SLuc Michel     case R_A2W_ ## pll_ ## _ANA1: \
2691e986e25SLuc Michel     case R_A2W_ ## pll_ ## _ANA2: \
2701e986e25SLuc Michel     case R_A2W_ ## pll_ ## _ANA3: \
2711e986e25SLuc Michel     case R_A2W_ ## pll_ ## _FRAC
2721e986e25SLuc Michel 
273fc14176bSLuc Michel static void cprman_write(void *opaque, hwaddr offset,
274fc14176bSLuc Michel                          uint64_t value, unsigned size)
275fc14176bSLuc Michel {
276fc14176bSLuc Michel     BCM2835CprmanState *s = CPRMAN(opaque);
277fc14176bSLuc Michel     size_t idx = offset / sizeof(uint32_t);
278fc14176bSLuc Michel 
279fc14176bSLuc Michel     if (FIELD_EX32(value, CPRMAN, PASSWORD) != CPRMAN_PASSWORD) {
280fc14176bSLuc Michel         trace_bcm2835_cprman_write_invalid_magic(offset, value);
281fc14176bSLuc Michel         return;
282fc14176bSLuc Michel     }
283fc14176bSLuc Michel 
284fc14176bSLuc Michel     value &= ~R_CPRMAN_PASSWORD_MASK;
285fc14176bSLuc Michel 
286fc14176bSLuc Michel     trace_bcm2835_cprman_write(offset, value);
287fc14176bSLuc Michel     s->regs[idx] = value;
288fc14176bSLuc Michel 
2891e986e25SLuc Michel     switch (idx) {
290*09d56bbcSLuc Michel     case R_CM_PLLA ... R_CM_PLLH:
291*09d56bbcSLuc Michel     case R_CM_PLLB:
292*09d56bbcSLuc Michel         /*
293*09d56bbcSLuc Michel          * A given CM_PLLx register is shared by both the PLL and the channels
294*09d56bbcSLuc Michel          * of this PLL.
295*09d56bbcSLuc Michel          */
296*09d56bbcSLuc Michel         update_pll_and_channels_from_cm(s, idx);
297*09d56bbcSLuc Michel         break;
298*09d56bbcSLuc Michel 
299*09d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLA) :
3001e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLA]);
3011e986e25SLuc Michel         break;
3021e986e25SLuc Michel 
303*09d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLC) :
3041e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLC]);
3051e986e25SLuc Michel         break;
3061e986e25SLuc Michel 
307*09d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLD) :
3081e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLD]);
3091e986e25SLuc Michel         break;
3101e986e25SLuc Michel 
311*09d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLH) :
3121e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLH]);
3131e986e25SLuc Michel         break;
3141e986e25SLuc Michel 
315*09d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLB) :
3161e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLB]);
3171e986e25SLuc Michel         break;
318*09d56bbcSLuc Michel 
319*09d56bbcSLuc Michel     case R_A2W_PLLA_DSI0:
320*09d56bbcSLuc Michel     case R_A2W_PLLA_CORE:
321*09d56bbcSLuc Michel     case R_A2W_PLLA_PER:
322*09d56bbcSLuc Michel     case R_A2W_PLLA_CCP2:
323*09d56bbcSLuc Michel     case R_A2W_PLLC_CORE2:
324*09d56bbcSLuc Michel     case R_A2W_PLLC_CORE1:
325*09d56bbcSLuc Michel     case R_A2W_PLLC_PER:
326*09d56bbcSLuc Michel     case R_A2W_PLLC_CORE0:
327*09d56bbcSLuc Michel     case R_A2W_PLLD_DSI0:
328*09d56bbcSLuc Michel     case R_A2W_PLLD_CORE:
329*09d56bbcSLuc Michel     case R_A2W_PLLD_PER:
330*09d56bbcSLuc Michel     case R_A2W_PLLD_DSI1:
331*09d56bbcSLuc Michel     case R_A2W_PLLH_AUX:
332*09d56bbcSLuc Michel     case R_A2W_PLLH_RCAL:
333*09d56bbcSLuc Michel     case R_A2W_PLLH_PIX:
334*09d56bbcSLuc Michel     case R_A2W_PLLB_ARM:
335*09d56bbcSLuc Michel         update_channel_from_a2w(s, idx);
336*09d56bbcSLuc Michel         break;
337fc14176bSLuc Michel     }
3381e986e25SLuc Michel }
3391e986e25SLuc Michel 
340*09d56bbcSLuc Michel #undef CASE_PLL_A2W_REGS
341fc14176bSLuc Michel 
342fc14176bSLuc Michel static const MemoryRegionOps cprman_ops = {
343fc14176bSLuc Michel     .read = cprman_read,
344fc14176bSLuc Michel     .write = cprman_write,
345fc14176bSLuc Michel     .endianness = DEVICE_LITTLE_ENDIAN,
346fc14176bSLuc Michel     .valid = {
347fc14176bSLuc Michel         /*
348fc14176bSLuc Michel          * Although this hasn't been checked against real hardware, nor the
349fc14176bSLuc Michel          * information can be found in a datasheet, it seems reasonable because
350fc14176bSLuc Michel          * of the "PASSWORD" magic value found in every registers.
351fc14176bSLuc Michel          */
352fc14176bSLuc Michel         .min_access_size        = 4,
353fc14176bSLuc Michel         .max_access_size        = 4,
354fc14176bSLuc Michel         .unaligned              = false,
355fc14176bSLuc Michel     },
356fc14176bSLuc Michel     .impl = {
357fc14176bSLuc Michel         .max_access_size = 4,
358fc14176bSLuc Michel     },
359fc14176bSLuc Michel };
360fc14176bSLuc Michel 
361fc14176bSLuc Michel static void cprman_reset(DeviceState *dev)
362fc14176bSLuc Michel {
363fc14176bSLuc Michel     BCM2835CprmanState *s = CPRMAN(dev);
3641e986e25SLuc Michel     size_t i;
365fc14176bSLuc Michel 
366fc14176bSLuc Michel     memset(s->regs, 0, sizeof(s->regs));
367fc14176bSLuc Michel 
3681e986e25SLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
3691e986e25SLuc Michel         device_cold_reset(DEVICE(&s->plls[i]));
3701e986e25SLuc Michel     }
3711e986e25SLuc Michel 
372*09d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
373*09d56bbcSLuc Michel         device_cold_reset(DEVICE(&s->channels[i]));
374*09d56bbcSLuc Michel     }
375*09d56bbcSLuc Michel 
376fc14176bSLuc Michel     clock_update_hz(s->xosc, s->xosc_freq);
377fc14176bSLuc Michel }
378fc14176bSLuc Michel 
379fc14176bSLuc Michel static void cprman_init(Object *obj)
380fc14176bSLuc Michel {
381fc14176bSLuc Michel     BCM2835CprmanState *s = CPRMAN(obj);
3821e986e25SLuc Michel     size_t i;
3831e986e25SLuc Michel 
3841e986e25SLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
3851e986e25SLuc Michel         object_initialize_child(obj, PLL_INIT_INFO[i].name,
3861e986e25SLuc Michel                                 &s->plls[i], TYPE_CPRMAN_PLL);
3871e986e25SLuc Michel         set_pll_init_info(s, &s->plls[i], i);
3881e986e25SLuc Michel     }
389fc14176bSLuc Michel 
390*09d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
391*09d56bbcSLuc Michel         object_initialize_child(obj, PLL_CHANNEL_INIT_INFO[i].name,
392*09d56bbcSLuc Michel                                 &s->channels[i],
393*09d56bbcSLuc Michel                                 TYPE_CPRMAN_PLL_CHANNEL);
394*09d56bbcSLuc Michel         set_pll_channel_init_info(s, &s->channels[i], i);
395*09d56bbcSLuc Michel     }
396*09d56bbcSLuc Michel 
397fc14176bSLuc Michel     s->xosc = clock_new(obj, "xosc");
398fc14176bSLuc Michel 
399fc14176bSLuc Michel     memory_region_init_io(&s->iomem, obj, &cprman_ops,
400fc14176bSLuc Michel                           s, "bcm2835-cprman", 0x2000);
401fc14176bSLuc Michel     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
402fc14176bSLuc Michel }
403fc14176bSLuc Michel 
4041e986e25SLuc Michel static void cprman_realize(DeviceState *dev, Error **errp)
4051e986e25SLuc Michel {
4061e986e25SLuc Michel     BCM2835CprmanState *s = CPRMAN(dev);
4071e986e25SLuc Michel     size_t i;
4081e986e25SLuc Michel 
4091e986e25SLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
4101e986e25SLuc Michel         CprmanPllState *pll = &s->plls[i];
4111e986e25SLuc Michel 
4121e986e25SLuc Michel         clock_set_source(pll->xosc_in, s->xosc);
4131e986e25SLuc Michel 
4141e986e25SLuc Michel         if (!qdev_realize(DEVICE(pll), NULL, errp)) {
4151e986e25SLuc Michel             return;
4161e986e25SLuc Michel         }
4171e986e25SLuc Michel     }
418*09d56bbcSLuc Michel 
419*09d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
420*09d56bbcSLuc Michel         CprmanPllChannelState *channel = &s->channels[i];
421*09d56bbcSLuc Michel         CprmanPll parent = PLL_CHANNEL_INIT_INFO[i].parent;
422*09d56bbcSLuc Michel         Clock *parent_clk = s->plls[parent].out;
423*09d56bbcSLuc Michel 
424*09d56bbcSLuc Michel         clock_set_source(channel->pll_in, parent_clk);
425*09d56bbcSLuc Michel 
426*09d56bbcSLuc Michel         if (!qdev_realize(DEVICE(channel), NULL, errp)) {
427*09d56bbcSLuc Michel             return;
428*09d56bbcSLuc Michel         }
429*09d56bbcSLuc Michel     }
4301e986e25SLuc Michel }
4311e986e25SLuc Michel 
432fc14176bSLuc Michel static const VMStateDescription cprman_vmstate = {
433fc14176bSLuc Michel     .name = TYPE_BCM2835_CPRMAN,
434fc14176bSLuc Michel     .version_id = 1,
435fc14176bSLuc Michel     .minimum_version_id = 1,
436fc14176bSLuc Michel     .fields = (VMStateField[]) {
437fc14176bSLuc Michel         VMSTATE_UINT32_ARRAY(regs, BCM2835CprmanState, CPRMAN_NUM_REGS),
438fc14176bSLuc Michel         VMSTATE_END_OF_LIST()
439fc14176bSLuc Michel     }
440fc14176bSLuc Michel };
441fc14176bSLuc Michel 
442fc14176bSLuc Michel static Property cprman_properties[] = {
443fc14176bSLuc Michel     DEFINE_PROP_UINT32("xosc-freq-hz", BCM2835CprmanState, xosc_freq, 19200000),
444fc14176bSLuc Michel     DEFINE_PROP_END_OF_LIST()
445fc14176bSLuc Michel };
446fc14176bSLuc Michel 
447fc14176bSLuc Michel static void cprman_class_init(ObjectClass *klass, void *data)
448fc14176bSLuc Michel {
449fc14176bSLuc Michel     DeviceClass *dc = DEVICE_CLASS(klass);
450fc14176bSLuc Michel 
4511e986e25SLuc Michel     dc->realize = cprman_realize;
452fc14176bSLuc Michel     dc->reset = cprman_reset;
453fc14176bSLuc Michel     dc->vmsd = &cprman_vmstate;
454fc14176bSLuc Michel     device_class_set_props(dc, cprman_properties);
455fc14176bSLuc Michel }
456fc14176bSLuc Michel 
457fc14176bSLuc Michel static const TypeInfo cprman_info = {
458fc14176bSLuc Michel     .name = TYPE_BCM2835_CPRMAN,
459fc14176bSLuc Michel     .parent = TYPE_SYS_BUS_DEVICE,
460fc14176bSLuc Michel     .instance_size = sizeof(BCM2835CprmanState),
461fc14176bSLuc Michel     .class_init = cprman_class_init,
462fc14176bSLuc Michel     .instance_init = cprman_init,
463fc14176bSLuc Michel };
464fc14176bSLuc Michel 
465fc14176bSLuc Michel static void cprman_register_types(void)
466fc14176bSLuc Michel {
467fc14176bSLuc Michel     type_register_static(&cprman_info);
4681e986e25SLuc Michel     type_register_static(&cprman_pll_info);
469*09d56bbcSLuc Michel     type_register_static(&cprman_pll_channel_info);
470fc14176bSLuc Michel }
471fc14176bSLuc Michel 
472fc14176bSLuc Michel type_init(cprman_register_types);
473