xref: /qemu/hw/misc/bcm2835_cprman.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
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.
4172813624SLuc Michel  *
4272813624SLuc Michel  * The CPRMAN exposes clock outputs with the name of the clock mux suffixed
4372813624SLuc Michel  * with "-out" (e.g. "uart-out", "h264-out", ...).
44fc14176bSLuc Michel  */
45fc14176bSLuc Michel 
46fc14176bSLuc Michel #include "qemu/osdep.h"
47fc14176bSLuc Michel #include "qemu/log.h"
48fc14176bSLuc Michel #include "migration/vmstate.h"
49fc14176bSLuc Michel #include "hw/qdev-properties.h"
50fc14176bSLuc Michel #include "hw/misc/bcm2835_cprman.h"
51fc14176bSLuc Michel #include "hw/misc/bcm2835_cprman_internals.h"
52fc14176bSLuc Michel #include "trace.h"
53fc14176bSLuc Michel 
541e986e25SLuc Michel /* PLL */
551e986e25SLuc Michel 
pll_reset(DeviceState * dev)5683ad4695SLuc Michel static void pll_reset(DeviceState *dev)
5783ad4695SLuc Michel {
5883ad4695SLuc Michel     CprmanPllState *s = CPRMAN_PLL(dev);
5983ad4695SLuc Michel     const PLLResetInfo *info = &PLL_RESET_INFO[s->id];
6083ad4695SLuc Michel 
6183ad4695SLuc Michel     *s->reg_cm = info->cm;
6283ad4695SLuc Michel     *s->reg_a2w_ctrl = info->a2w_ctrl;
6383ad4695SLuc Michel     memcpy(s->reg_a2w_ana, info->a2w_ana, sizeof(info->a2w_ana));
6483ad4695SLuc Michel     *s->reg_a2w_frac = info->a2w_frac;
6583ad4695SLuc Michel }
6683ad4695SLuc Michel 
pll_is_locked(const CprmanPllState * pll)676d2b874cSLuc Michel static bool pll_is_locked(const CprmanPllState *pll)
686d2b874cSLuc Michel {
696d2b874cSLuc Michel     return !FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PWRDN)
706d2b874cSLuc Michel         && !FIELD_EX32(*pll->reg_cm, CM_PLLx, ANARST);
716d2b874cSLuc Michel }
726d2b874cSLuc Michel 
pll_update(CprmanPllState * pll)731e986e25SLuc Michel static void pll_update(CprmanPllState *pll)
741e986e25SLuc Michel {
756d2b874cSLuc Michel     uint64_t freq, ndiv, fdiv, pdiv;
766d2b874cSLuc Michel 
776d2b874cSLuc Michel     if (!pll_is_locked(pll)) {
781e986e25SLuc Michel         clock_update(pll->out, 0);
796d2b874cSLuc Michel         return;
806d2b874cSLuc Michel     }
816d2b874cSLuc Michel 
826d2b874cSLuc Michel     pdiv = FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PDIV);
836d2b874cSLuc Michel 
846d2b874cSLuc Michel     if (!pdiv) {
856d2b874cSLuc Michel         clock_update(pll->out, 0);
866d2b874cSLuc Michel         return;
876d2b874cSLuc Michel     }
886d2b874cSLuc Michel 
896d2b874cSLuc Michel     ndiv = FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, NDIV);
906d2b874cSLuc Michel     fdiv = FIELD_EX32(*pll->reg_a2w_frac, A2W_PLLx_FRAC, FRAC);
916d2b874cSLuc Michel 
926d2b874cSLuc Michel     if (pll->reg_a2w_ana[1] & pll->prediv_mask) {
936d2b874cSLuc Michel         /* The prescaler doubles the parent frequency */
946d2b874cSLuc Michel         ndiv *= 2;
956d2b874cSLuc Michel         fdiv *= 2;
966d2b874cSLuc Michel     }
976d2b874cSLuc Michel 
986d2b874cSLuc Michel     /*
996d2b874cSLuc Michel      * We have a multiplier with an integer part (ndiv) and a fractional part
1006d2b874cSLuc Michel      * (fdiv), and a divider (pdiv).
1016d2b874cSLuc Michel      */
1026d2b874cSLuc Michel     freq = clock_get_hz(pll->xosc_in) *
1036d2b874cSLuc Michel         ((ndiv << R_A2W_PLLx_FRAC_FRAC_LENGTH) + fdiv);
1046d2b874cSLuc Michel     freq /= pdiv;
1056d2b874cSLuc Michel     freq >>= R_A2W_PLLx_FRAC_FRAC_LENGTH;
1066d2b874cSLuc Michel 
1076d2b874cSLuc Michel     clock_update_hz(pll->out, freq);
1081e986e25SLuc Michel }
1091e986e25SLuc Michel 
pll_xosc_update(void * opaque,ClockEvent event)1105ee0abedSPeter Maydell static void pll_xosc_update(void *opaque, ClockEvent event)
1111e986e25SLuc Michel {
1121e986e25SLuc Michel     pll_update(CPRMAN_PLL(opaque));
1131e986e25SLuc Michel }
1141e986e25SLuc Michel 
pll_init(Object * obj)1151e986e25SLuc Michel static void pll_init(Object *obj)
1161e986e25SLuc Michel {
1171e986e25SLuc Michel     CprmanPllState *s = CPRMAN_PLL(obj);
1181e986e25SLuc Michel 
1195ee0abedSPeter Maydell     s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update,
1205ee0abedSPeter Maydell                                     s, ClockUpdate);
1211e986e25SLuc Michel     s->out = qdev_init_clock_out(DEVICE(s), "out");
1221e986e25SLuc Michel }
1231e986e25SLuc Michel 
1241e986e25SLuc Michel static const VMStateDescription pll_vmstate = {
1251e986e25SLuc Michel     .name = TYPE_CPRMAN_PLL,
1261e986e25SLuc Michel     .version_id = 1,
1271e986e25SLuc Michel     .minimum_version_id = 1,
128e4ea952fSRichard Henderson     .fields = (const VMStateField[]) {
1291e986e25SLuc Michel         VMSTATE_CLOCK(xosc_in, CprmanPllState),
1301e986e25SLuc Michel         VMSTATE_END_OF_LIST()
1311e986e25SLuc Michel     }
1321e986e25SLuc Michel };
1331e986e25SLuc Michel 
pll_class_init(ObjectClass * klass,const void * data)134*12d1a768SPhilippe Mathieu-Daudé static void pll_class_init(ObjectClass *klass, const void *data)
1351e986e25SLuc Michel {
1361e986e25SLuc Michel     DeviceClass *dc = DEVICE_CLASS(klass);
1371e986e25SLuc Michel 
138e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, pll_reset);
1391e986e25SLuc Michel     dc->vmsd = &pll_vmstate;
140490aaae9SPhilippe Mathieu-Daudé     /* Reason: Part of BCM2835CprmanState component */
141490aaae9SPhilippe Mathieu-Daudé     dc->user_creatable = false;
1421e986e25SLuc Michel }
1431e986e25SLuc Michel 
1441e986e25SLuc Michel static const TypeInfo cprman_pll_info = {
1451e986e25SLuc Michel     .name = TYPE_CPRMAN_PLL,
1461e986e25SLuc Michel     .parent = TYPE_DEVICE,
1471e986e25SLuc Michel     .instance_size = sizeof(CprmanPllState),
1481e986e25SLuc Michel     .class_init = pll_class_init,
1491e986e25SLuc Michel     .instance_init = pll_init,
1501e986e25SLuc Michel };
1511e986e25SLuc Michel 
1521e986e25SLuc Michel 
15309d56bbcSLuc Michel /* PLL channel */
15409d56bbcSLuc Michel 
pll_channel_reset(DeviceState * dev)15583ad4695SLuc Michel static void pll_channel_reset(DeviceState *dev)
15683ad4695SLuc Michel {
15783ad4695SLuc Michel     CprmanPllChannelState *s = CPRMAN_PLL_CHANNEL(dev);
15883ad4695SLuc Michel     const PLLChannelResetInfo *info = &PLL_CHANNEL_RESET_INFO[s->id];
15983ad4695SLuc Michel 
16083ad4695SLuc Michel     *s->reg_a2w_ctrl = info->a2w_ctrl;
16183ad4695SLuc Michel }
16283ad4695SLuc Michel 
pll_channel_is_enabled(CprmanPllChannelState * channel)16395745811SLuc Michel static bool pll_channel_is_enabled(CprmanPllChannelState *channel)
16495745811SLuc Michel {
16595745811SLuc Michel     /*
16695745811SLuc Michel      * XXX I'm not sure of the purpose of the LOAD field. The Linux driver does
16795745811SLuc Michel      * not set it when enabling the channel, but does clear it when disabling
16895745811SLuc Michel      * it.
16995745811SLuc Michel      */
17095745811SLuc Michel     return !FIELD_EX32(*channel->reg_a2w_ctrl, A2W_PLLx_CHANNELy, DISABLE)
17195745811SLuc Michel         && !(*channel->reg_cm & channel->hold_mask);
17295745811SLuc Michel }
17395745811SLuc Michel 
pll_channel_update(CprmanPllChannelState * channel)17409d56bbcSLuc Michel static void pll_channel_update(CprmanPllChannelState *channel)
17509d56bbcSLuc Michel {
17695745811SLuc Michel     uint64_t freq, div;
17795745811SLuc Michel 
17895745811SLuc Michel     if (!pll_channel_is_enabled(channel)) {
17909d56bbcSLuc Michel         clock_update(channel->out, 0);
18095745811SLuc Michel         return;
18195745811SLuc Michel     }
18295745811SLuc Michel 
18395745811SLuc Michel     div = FIELD_EX32(*channel->reg_a2w_ctrl, A2W_PLLx_CHANNELy, DIV);
18495745811SLuc Michel 
18595745811SLuc Michel     if (!div) {
18695745811SLuc Michel         /*
18795745811SLuc Michel          * It seems that when the divider value is 0, it is considered as
18895745811SLuc Michel          * being maximum by the hardware (see the Linux driver).
18995745811SLuc Michel          */
19095745811SLuc Michel         div = R_A2W_PLLx_CHANNELy_DIV_MASK;
19195745811SLuc Michel     }
19295745811SLuc Michel 
19395745811SLuc Michel     /* Some channels have an additional fixed divider */
19495745811SLuc Michel     freq = clock_get_hz(channel->pll_in) / (div * channel->fixed_divider);
19595745811SLuc Michel 
19695745811SLuc Michel     clock_update_hz(channel->out, freq);
19709d56bbcSLuc Michel }
19809d56bbcSLuc Michel 
19909d56bbcSLuc Michel /* Update a PLL and all its channels */
pll_update_all_channels(BCM2835CprmanState * s,CprmanPllState * pll)20009d56bbcSLuc Michel static void pll_update_all_channels(BCM2835CprmanState *s,
20109d56bbcSLuc Michel                                     CprmanPllState *pll)
20209d56bbcSLuc Michel {
20309d56bbcSLuc Michel     size_t i;
20409d56bbcSLuc Michel 
20509d56bbcSLuc Michel     pll_update(pll);
20609d56bbcSLuc Michel 
20709d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
20809d56bbcSLuc Michel         CprmanPllChannelState *channel = &s->channels[i];
20909d56bbcSLuc Michel         if (channel->parent == pll->id) {
21009d56bbcSLuc Michel             pll_channel_update(channel);
21109d56bbcSLuc Michel         }
21209d56bbcSLuc Michel     }
21309d56bbcSLuc Michel }
21409d56bbcSLuc Michel 
pll_channel_pll_in_update(void * opaque,ClockEvent event)2155ee0abedSPeter Maydell static void pll_channel_pll_in_update(void *opaque, ClockEvent event)
21609d56bbcSLuc Michel {
21709d56bbcSLuc Michel     pll_channel_update(CPRMAN_PLL_CHANNEL(opaque));
21809d56bbcSLuc Michel }
21909d56bbcSLuc Michel 
pll_channel_init(Object * obj)22009d56bbcSLuc Michel static void pll_channel_init(Object *obj)
22109d56bbcSLuc Michel {
22209d56bbcSLuc Michel     CprmanPllChannelState *s = CPRMAN_PLL_CHANNEL(obj);
22309d56bbcSLuc Michel 
22409d56bbcSLuc Michel     s->pll_in = qdev_init_clock_in(DEVICE(s), "pll-in",
2255ee0abedSPeter Maydell                                    pll_channel_pll_in_update, s,
2265ee0abedSPeter Maydell                                    ClockUpdate);
22709d56bbcSLuc Michel     s->out = qdev_init_clock_out(DEVICE(s), "out");
22809d56bbcSLuc Michel }
22909d56bbcSLuc Michel 
23009d56bbcSLuc Michel static const VMStateDescription pll_channel_vmstate = {
23109d56bbcSLuc Michel     .name = TYPE_CPRMAN_PLL_CHANNEL,
23209d56bbcSLuc Michel     .version_id = 1,
23309d56bbcSLuc Michel     .minimum_version_id = 1,
234e4ea952fSRichard Henderson     .fields = (const VMStateField[]) {
23509d56bbcSLuc Michel         VMSTATE_CLOCK(pll_in, CprmanPllChannelState),
23609d56bbcSLuc Michel         VMSTATE_END_OF_LIST()
23709d56bbcSLuc Michel     }
23809d56bbcSLuc Michel };
23909d56bbcSLuc Michel 
pll_channel_class_init(ObjectClass * klass,const void * data)240*12d1a768SPhilippe Mathieu-Daudé static void pll_channel_class_init(ObjectClass *klass, const void *data)
24109d56bbcSLuc Michel {
24209d56bbcSLuc Michel     DeviceClass *dc = DEVICE_CLASS(klass);
24309d56bbcSLuc Michel 
244e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, pll_channel_reset);
24509d56bbcSLuc Michel     dc->vmsd = &pll_channel_vmstate;
246490aaae9SPhilippe Mathieu-Daudé     /* Reason: Part of BCM2835CprmanState component */
247490aaae9SPhilippe Mathieu-Daudé     dc->user_creatable = false;
24809d56bbcSLuc Michel }
24909d56bbcSLuc Michel 
25009d56bbcSLuc Michel static const TypeInfo cprman_pll_channel_info = {
25109d56bbcSLuc Michel     .name = TYPE_CPRMAN_PLL_CHANNEL,
25209d56bbcSLuc Michel     .parent = TYPE_DEVICE,
25309d56bbcSLuc Michel     .instance_size = sizeof(CprmanPllChannelState),
25409d56bbcSLuc Michel     .class_init = pll_channel_class_init,
25509d56bbcSLuc Michel     .instance_init = pll_channel_init,
25609d56bbcSLuc Michel };
25709d56bbcSLuc Michel 
25809d56bbcSLuc Michel 
25972813624SLuc Michel /* clock mux */
26072813624SLuc Michel 
clock_mux_is_enabled(CprmanClockMuxState * mux)261fc984085SLuc Michel static bool clock_mux_is_enabled(CprmanClockMuxState *mux)
262fc984085SLuc Michel {
263fc984085SLuc Michel     return FIELD_EX32(*mux->reg_ctl, CM_CLOCKx_CTL, ENABLE);
264fc984085SLuc Michel }
265fc984085SLuc Michel 
clock_mux_update(CprmanClockMuxState * mux)26672813624SLuc Michel static void clock_mux_update(CprmanClockMuxState *mux)
26772813624SLuc Michel {
268fc984085SLuc Michel     uint64_t freq;
269fc984085SLuc Michel     uint32_t div, src = FIELD_EX32(*mux->reg_ctl, CM_CLOCKx_CTL, SRC);
270fc984085SLuc Michel     bool enabled = clock_mux_is_enabled(mux);
271fc984085SLuc Michel 
272fc984085SLuc Michel     *mux->reg_ctl = FIELD_DP32(*mux->reg_ctl, CM_CLOCKx_CTL, BUSY, enabled);
273fc984085SLuc Michel 
274fc984085SLuc Michel     if (!enabled) {
27572813624SLuc Michel         clock_update(mux->out, 0);
276fc984085SLuc Michel         return;
277fc984085SLuc Michel     }
278fc984085SLuc Michel 
279fc984085SLuc Michel     freq = clock_get_hz(mux->srcs[src]);
280fc984085SLuc Michel 
281fc984085SLuc Michel     if (mux->int_bits == 0 && mux->frac_bits == 0) {
282fc984085SLuc Michel         clock_update_hz(mux->out, freq);
283fc984085SLuc Michel         return;
284fc984085SLuc Michel     }
285fc984085SLuc Michel 
286fc984085SLuc Michel     /*
287fc984085SLuc Michel      * The divider has an integer and a fractional part. The size of each part
288fc984085SLuc Michel      * varies with the muxes (int_bits and frac_bits). Both parts are
289fc984085SLuc Michel      * concatenated, with the integer part always starting at bit 12.
290fc984085SLuc Michel      *
291fc984085SLuc Michel      *         31          12 11          0
292fc984085SLuc Michel      *        ------------------------------
293fc984085SLuc Michel      * CM_DIV |      |  int  |  frac  |    |
294fc984085SLuc Michel      *        ------------------------------
295fc984085SLuc Michel      *                <-----> <------>
296fc984085SLuc Michel      *                int_bits frac_bits
297fc984085SLuc Michel      */
298fc984085SLuc Michel     div = extract32(*mux->reg_div,
299fc984085SLuc Michel                     R_CM_CLOCKx_DIV_FRAC_LENGTH - mux->frac_bits,
300fc984085SLuc Michel                     mux->int_bits + mux->frac_bits);
301fc984085SLuc Michel 
302fc984085SLuc Michel     if (!div) {
303fc984085SLuc Michel         clock_update(mux->out, 0);
304fc984085SLuc Michel         return;
305fc984085SLuc Michel     }
306fc984085SLuc Michel 
307fc984085SLuc Michel     freq = muldiv64(freq, 1 << mux->frac_bits, div);
308fc984085SLuc Michel 
309fc984085SLuc Michel     clock_update_hz(mux->out, freq);
31072813624SLuc Michel }
31172813624SLuc Michel 
clock_mux_src_update(void * opaque,ClockEvent event)3125ee0abedSPeter Maydell static void clock_mux_src_update(void *opaque, ClockEvent event)
31372813624SLuc Michel {
31472813624SLuc Michel     CprmanClockMuxState **backref = opaque;
31572813624SLuc Michel     CprmanClockMuxState *s = *backref;
316fc984085SLuc Michel     CprmanClockMuxSource src = backref - s->backref;
317fc984085SLuc Michel 
318fc984085SLuc Michel     if (FIELD_EX32(*s->reg_ctl, CM_CLOCKx_CTL, SRC) != src) {
319fc984085SLuc Michel         return;
320fc984085SLuc Michel     }
32172813624SLuc Michel 
32272813624SLuc Michel     clock_mux_update(s);
32372813624SLuc Michel }
32472813624SLuc Michel 
clock_mux_reset(DeviceState * dev)32583ad4695SLuc Michel static void clock_mux_reset(DeviceState *dev)
32683ad4695SLuc Michel {
32783ad4695SLuc Michel     CprmanClockMuxState *clock = CPRMAN_CLOCK_MUX(dev);
32883ad4695SLuc Michel     const ClockMuxResetInfo *info = &CLOCK_MUX_RESET_INFO[clock->id];
32983ad4695SLuc Michel 
33083ad4695SLuc Michel     *clock->reg_ctl = info->cm_ctl;
33183ad4695SLuc Michel     *clock->reg_div = info->cm_div;
33283ad4695SLuc Michel }
33383ad4695SLuc Michel 
clock_mux_init(Object * obj)33472813624SLuc Michel static void clock_mux_init(Object *obj)
33572813624SLuc Michel {
33672813624SLuc Michel     CprmanClockMuxState *s = CPRMAN_CLOCK_MUX(obj);
33772813624SLuc Michel     size_t i;
33872813624SLuc Michel 
33972813624SLuc Michel     for (i = 0; i < CPRMAN_NUM_CLOCK_MUX_SRC; i++) {
34072813624SLuc Michel         char *name = g_strdup_printf("srcs[%zu]", i);
34172813624SLuc Michel         s->backref[i] = s;
34272813624SLuc Michel         s->srcs[i] = qdev_init_clock_in(DEVICE(s), name,
34372813624SLuc Michel                                         clock_mux_src_update,
3445ee0abedSPeter Maydell                                         &s->backref[i],
3455ee0abedSPeter Maydell                                         ClockUpdate);
34672813624SLuc Michel         g_free(name);
34772813624SLuc Michel     }
34872813624SLuc Michel 
34972813624SLuc Michel     s->out = qdev_init_clock_out(DEVICE(s), "out");
35072813624SLuc Michel }
35172813624SLuc Michel 
35272813624SLuc Michel static const VMStateDescription clock_mux_vmstate = {
35372813624SLuc Michel     .name = TYPE_CPRMAN_CLOCK_MUX,
35472813624SLuc Michel     .version_id = 1,
35572813624SLuc Michel     .minimum_version_id = 1,
356e4ea952fSRichard Henderson     .fields = (const VMStateField[]) {
35772813624SLuc Michel         VMSTATE_ARRAY_CLOCK(srcs, CprmanClockMuxState,
35872813624SLuc Michel                             CPRMAN_NUM_CLOCK_MUX_SRC),
35972813624SLuc Michel         VMSTATE_END_OF_LIST()
36072813624SLuc Michel     }
36172813624SLuc Michel };
36272813624SLuc Michel 
clock_mux_class_init(ObjectClass * klass,const void * data)363*12d1a768SPhilippe Mathieu-Daudé static void clock_mux_class_init(ObjectClass *klass, const void *data)
36472813624SLuc Michel {
36572813624SLuc Michel     DeviceClass *dc = DEVICE_CLASS(klass);
36672813624SLuc Michel 
367e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, clock_mux_reset);
36872813624SLuc Michel     dc->vmsd = &clock_mux_vmstate;
369490aaae9SPhilippe Mathieu-Daudé     /* Reason: Part of BCM2835CprmanState component */
370490aaae9SPhilippe Mathieu-Daudé     dc->user_creatable = false;
37172813624SLuc Michel }
37272813624SLuc Michel 
37372813624SLuc Michel static const TypeInfo cprman_clock_mux_info = {
37472813624SLuc Michel     .name = TYPE_CPRMAN_CLOCK_MUX,
37572813624SLuc Michel     .parent = TYPE_DEVICE,
37672813624SLuc Michel     .instance_size = sizeof(CprmanClockMuxState),
37772813624SLuc Michel     .class_init = clock_mux_class_init,
37872813624SLuc Michel     .instance_init = clock_mux_init,
37972813624SLuc Michel };
38072813624SLuc Michel 
38172813624SLuc Michel 
382502960caSLuc Michel /* DSI0HSCK mux */
383502960caSLuc Michel 
dsi0hsck_mux_update(CprmanDsi0HsckMuxState * s)384502960caSLuc Michel static void dsi0hsck_mux_update(CprmanDsi0HsckMuxState *s)
385502960caSLuc Michel {
386502960caSLuc Michel     bool src_is_plld = FIELD_EX32(*s->reg_cm, CM_DSI0HSCK, SELPLLD);
387502960caSLuc Michel     Clock *src = src_is_plld ? s->plld_in : s->plla_in;
388502960caSLuc Michel 
389502960caSLuc Michel     clock_update(s->out, clock_get(src));
390502960caSLuc Michel }
391502960caSLuc Michel 
dsi0hsck_mux_in_update(void * opaque,ClockEvent event)3925ee0abedSPeter Maydell static void dsi0hsck_mux_in_update(void *opaque, ClockEvent event)
393502960caSLuc Michel {
394502960caSLuc Michel     dsi0hsck_mux_update(CPRMAN_DSI0HSCK_MUX(opaque));
395502960caSLuc Michel }
396502960caSLuc Michel 
dsi0hsck_mux_init(Object * obj)397502960caSLuc Michel static void dsi0hsck_mux_init(Object *obj)
398502960caSLuc Michel {
399502960caSLuc Michel     CprmanDsi0HsckMuxState *s = CPRMAN_DSI0HSCK_MUX(obj);
400502960caSLuc Michel     DeviceState *dev = DEVICE(obj);
401502960caSLuc Michel 
4025ee0abedSPeter Maydell     s->plla_in = qdev_init_clock_in(dev, "plla-in", dsi0hsck_mux_in_update,
4035ee0abedSPeter Maydell                                     s, ClockUpdate);
4045ee0abedSPeter Maydell     s->plld_in = qdev_init_clock_in(dev, "plld-in", dsi0hsck_mux_in_update,
4055ee0abedSPeter Maydell                                     s, ClockUpdate);
406502960caSLuc Michel     s->out = qdev_init_clock_out(DEVICE(s), "out");
407502960caSLuc Michel }
408502960caSLuc Michel 
409502960caSLuc Michel static const VMStateDescription dsi0hsck_mux_vmstate = {
410502960caSLuc Michel     .name = TYPE_CPRMAN_DSI0HSCK_MUX,
411502960caSLuc Michel     .version_id = 1,
412502960caSLuc Michel     .minimum_version_id = 1,
413e4ea952fSRichard Henderson     .fields = (const VMStateField[]) {
414502960caSLuc Michel         VMSTATE_CLOCK(plla_in, CprmanDsi0HsckMuxState),
415502960caSLuc Michel         VMSTATE_CLOCK(plld_in, CprmanDsi0HsckMuxState),
416502960caSLuc Michel         VMSTATE_END_OF_LIST()
417502960caSLuc Michel     }
418502960caSLuc Michel };
419502960caSLuc Michel 
dsi0hsck_mux_class_init(ObjectClass * klass,const void * data)420*12d1a768SPhilippe Mathieu-Daudé static void dsi0hsck_mux_class_init(ObjectClass *klass, const void *data)
421502960caSLuc Michel {
422502960caSLuc Michel     DeviceClass *dc = DEVICE_CLASS(klass);
423502960caSLuc Michel 
424502960caSLuc Michel     dc->vmsd = &dsi0hsck_mux_vmstate;
425490aaae9SPhilippe Mathieu-Daudé     /* Reason: Part of BCM2835CprmanState component */
426490aaae9SPhilippe Mathieu-Daudé     dc->user_creatable = false;
427502960caSLuc Michel }
428502960caSLuc Michel 
429502960caSLuc Michel static const TypeInfo cprman_dsi0hsck_mux_info = {
430502960caSLuc Michel     .name = TYPE_CPRMAN_DSI0HSCK_MUX,
431502960caSLuc Michel     .parent = TYPE_DEVICE,
432502960caSLuc Michel     .instance_size = sizeof(CprmanDsi0HsckMuxState),
433502960caSLuc Michel     .class_init = dsi0hsck_mux_class_init,
434502960caSLuc Michel     .instance_init = dsi0hsck_mux_init,
435502960caSLuc Michel };
436502960caSLuc Michel 
437502960caSLuc Michel 
438fc14176bSLuc Michel /* CPRMAN "top level" model */
439fc14176bSLuc Michel 
get_cm_lock(const BCM2835CprmanState * s)4406d2b874cSLuc Michel static uint32_t get_cm_lock(const BCM2835CprmanState *s)
4416d2b874cSLuc Michel {
4426d2b874cSLuc Michel     static const int CM_LOCK_MAPPING[CPRMAN_NUM_PLL] = {
4436d2b874cSLuc Michel         [CPRMAN_PLLA] = R_CM_LOCK_FLOCKA_SHIFT,
4446d2b874cSLuc Michel         [CPRMAN_PLLC] = R_CM_LOCK_FLOCKC_SHIFT,
4456d2b874cSLuc Michel         [CPRMAN_PLLD] = R_CM_LOCK_FLOCKD_SHIFT,
4466d2b874cSLuc Michel         [CPRMAN_PLLH] = R_CM_LOCK_FLOCKH_SHIFT,
4476d2b874cSLuc Michel         [CPRMAN_PLLB] = R_CM_LOCK_FLOCKB_SHIFT,
4486d2b874cSLuc Michel     };
4496d2b874cSLuc Michel 
4506d2b874cSLuc Michel     uint32_t r = 0;
4516d2b874cSLuc Michel     size_t i;
4526d2b874cSLuc Michel 
4536d2b874cSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
4546d2b874cSLuc Michel         r |= pll_is_locked(&s->plls[i]) << CM_LOCK_MAPPING[i];
4556d2b874cSLuc Michel     }
4566d2b874cSLuc Michel 
4576d2b874cSLuc Michel     return r;
4586d2b874cSLuc Michel }
4596d2b874cSLuc Michel 
cprman_read(void * opaque,hwaddr offset,unsigned size)460fc14176bSLuc Michel static uint64_t cprman_read(void *opaque, hwaddr offset,
461fc14176bSLuc Michel                             unsigned size)
462fc14176bSLuc Michel {
463fc14176bSLuc Michel     BCM2835CprmanState *s = CPRMAN(opaque);
464fc14176bSLuc Michel     uint64_t r = 0;
465fc14176bSLuc Michel     size_t idx = offset / sizeof(uint32_t);
466fc14176bSLuc Michel 
467fc14176bSLuc Michel     switch (idx) {
4686d2b874cSLuc Michel     case R_CM_LOCK:
4696d2b874cSLuc Michel         r = get_cm_lock(s);
4706d2b874cSLuc Michel         break;
4716d2b874cSLuc Michel 
472fc14176bSLuc Michel     default:
473fc14176bSLuc Michel         r = s->regs[idx];
474fc14176bSLuc Michel     }
475fc14176bSLuc Michel 
476fc14176bSLuc Michel     trace_bcm2835_cprman_read(offset, r);
477fc14176bSLuc Michel     return r;
478fc14176bSLuc Michel }
479fc14176bSLuc Michel 
update_pll_and_channels_from_cm(BCM2835CprmanState * s,size_t idx)48009d56bbcSLuc Michel static inline void update_pll_and_channels_from_cm(BCM2835CprmanState *s,
48109d56bbcSLuc Michel                                                    size_t idx)
48209d56bbcSLuc Michel {
48309d56bbcSLuc Michel     size_t i;
48409d56bbcSLuc Michel 
48509d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
48609d56bbcSLuc Michel         if (PLL_INIT_INFO[i].cm_offset == idx) {
48709d56bbcSLuc Michel             pll_update_all_channels(s, &s->plls[i]);
48809d56bbcSLuc Michel             return;
48909d56bbcSLuc Michel         }
49009d56bbcSLuc Michel     }
49109d56bbcSLuc Michel }
49209d56bbcSLuc Michel 
update_channel_from_a2w(BCM2835CprmanState * s,size_t idx)49309d56bbcSLuc Michel static inline void update_channel_from_a2w(BCM2835CprmanState *s, size_t idx)
49409d56bbcSLuc Michel {
49509d56bbcSLuc Michel     size_t i;
49609d56bbcSLuc Michel 
49709d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
49809d56bbcSLuc Michel         if (PLL_CHANNEL_INIT_INFO[i].a2w_ctrl_offset == idx) {
49909d56bbcSLuc Michel             pll_channel_update(&s->channels[i]);
50009d56bbcSLuc Michel             return;
50109d56bbcSLuc Michel         }
50209d56bbcSLuc Michel     }
50309d56bbcSLuc Michel }
50409d56bbcSLuc Michel 
update_mux_from_cm(BCM2835CprmanState * s,size_t idx)50572813624SLuc Michel static inline void update_mux_from_cm(BCM2835CprmanState *s, size_t idx)
50672813624SLuc Michel {
50772813624SLuc Michel     size_t i;
50872813624SLuc Michel 
50972813624SLuc Michel     for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) {
51072813624SLuc Michel         if ((CLOCK_MUX_INIT_INFO[i].cm_offset == idx) ||
51172813624SLuc Michel             (CLOCK_MUX_INIT_INFO[i].cm_offset + 4 == idx)) {
51272813624SLuc Michel             /* matches CM_CTL or CM_DIV mux register */
51372813624SLuc Michel             clock_mux_update(&s->clock_muxes[i]);
51472813624SLuc Michel             return;
51572813624SLuc Michel         }
51672813624SLuc Michel     }
51772813624SLuc Michel }
51872813624SLuc Michel 
51909d56bbcSLuc Michel #define CASE_PLL_A2W_REGS(pll_) \
5201e986e25SLuc Michel     case R_A2W_ ## pll_ ## _CTRL: \
5211e986e25SLuc Michel     case R_A2W_ ## pll_ ## _ANA0: \
5221e986e25SLuc Michel     case R_A2W_ ## pll_ ## _ANA1: \
5231e986e25SLuc Michel     case R_A2W_ ## pll_ ## _ANA2: \
5241e986e25SLuc Michel     case R_A2W_ ## pll_ ## _ANA3: \
5251e986e25SLuc Michel     case R_A2W_ ## pll_ ## _FRAC
5261e986e25SLuc Michel 
cprman_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)527fc14176bSLuc Michel static void cprman_write(void *opaque, hwaddr offset,
528fc14176bSLuc Michel                          uint64_t value, unsigned size)
529fc14176bSLuc Michel {
530fc14176bSLuc Michel     BCM2835CprmanState *s = CPRMAN(opaque);
531fc14176bSLuc Michel     size_t idx = offset / sizeof(uint32_t);
532fc14176bSLuc Michel 
533fc14176bSLuc Michel     if (FIELD_EX32(value, CPRMAN, PASSWORD) != CPRMAN_PASSWORD) {
534fc14176bSLuc Michel         trace_bcm2835_cprman_write_invalid_magic(offset, value);
535fc14176bSLuc Michel         return;
536fc14176bSLuc Michel     }
537fc14176bSLuc Michel 
538fc14176bSLuc Michel     value &= ~R_CPRMAN_PASSWORD_MASK;
539fc14176bSLuc Michel 
540fc14176bSLuc Michel     trace_bcm2835_cprman_write(offset, value);
541fc14176bSLuc Michel     s->regs[idx] = value;
542fc14176bSLuc Michel 
5431e986e25SLuc Michel     switch (idx) {
54409d56bbcSLuc Michel     case R_CM_PLLA ... R_CM_PLLH:
54509d56bbcSLuc Michel     case R_CM_PLLB:
54609d56bbcSLuc Michel         /*
54709d56bbcSLuc Michel          * A given CM_PLLx register is shared by both the PLL and the channels
54809d56bbcSLuc Michel          * of this PLL.
54909d56bbcSLuc Michel          */
55009d56bbcSLuc Michel         update_pll_and_channels_from_cm(s, idx);
55109d56bbcSLuc Michel         break;
55209d56bbcSLuc Michel 
55309d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLA) :
5541e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLA]);
5551e986e25SLuc Michel         break;
5561e986e25SLuc Michel 
55709d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLC) :
5581e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLC]);
5591e986e25SLuc Michel         break;
5601e986e25SLuc Michel 
56109d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLD) :
5621e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLD]);
5631e986e25SLuc Michel         break;
5641e986e25SLuc Michel 
56509d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLH) :
5661e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLH]);
5671e986e25SLuc Michel         break;
5681e986e25SLuc Michel 
56909d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLB) :
5701e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLB]);
5711e986e25SLuc Michel         break;
57209d56bbcSLuc Michel 
57309d56bbcSLuc Michel     case R_A2W_PLLA_DSI0:
57409d56bbcSLuc Michel     case R_A2W_PLLA_CORE:
57509d56bbcSLuc Michel     case R_A2W_PLLA_PER:
57609d56bbcSLuc Michel     case R_A2W_PLLA_CCP2:
57709d56bbcSLuc Michel     case R_A2W_PLLC_CORE2:
57809d56bbcSLuc Michel     case R_A2W_PLLC_CORE1:
57909d56bbcSLuc Michel     case R_A2W_PLLC_PER:
58009d56bbcSLuc Michel     case R_A2W_PLLC_CORE0:
58109d56bbcSLuc Michel     case R_A2W_PLLD_DSI0:
58209d56bbcSLuc Michel     case R_A2W_PLLD_CORE:
58309d56bbcSLuc Michel     case R_A2W_PLLD_PER:
58409d56bbcSLuc Michel     case R_A2W_PLLD_DSI1:
58509d56bbcSLuc Michel     case R_A2W_PLLH_AUX:
58609d56bbcSLuc Michel     case R_A2W_PLLH_RCAL:
58709d56bbcSLuc Michel     case R_A2W_PLLH_PIX:
58809d56bbcSLuc Michel     case R_A2W_PLLB_ARM:
58909d56bbcSLuc Michel         update_channel_from_a2w(s, idx);
59009d56bbcSLuc Michel         break;
59172813624SLuc Michel 
59272813624SLuc Michel     case R_CM_GNRICCTL ... R_CM_SMIDIV:
59372813624SLuc Michel     case R_CM_TCNTCNT ... R_CM_VECDIV:
59472813624SLuc Michel     case R_CM_PULSECTL ... R_CM_PULSEDIV:
59572813624SLuc Michel     case R_CM_SDCCTL ... R_CM_ARMCTL:
59672813624SLuc Michel     case R_CM_AVEOCTL ... R_CM_EMMCDIV:
59772813624SLuc Michel     case R_CM_EMMC2CTL ... R_CM_EMMC2DIV:
59872813624SLuc Michel         update_mux_from_cm(s, idx);
59972813624SLuc Michel         break;
600502960caSLuc Michel 
601502960caSLuc Michel     case R_CM_DSI0HSCK:
602502960caSLuc Michel         dsi0hsck_mux_update(&s->dsi0hsck_mux);
603502960caSLuc Michel         break;
604fc14176bSLuc Michel     }
6051e986e25SLuc Michel }
6061e986e25SLuc Michel 
60709d56bbcSLuc Michel #undef CASE_PLL_A2W_REGS
608fc14176bSLuc Michel 
609fc14176bSLuc Michel static const MemoryRegionOps cprman_ops = {
610fc14176bSLuc Michel     .read = cprman_read,
611fc14176bSLuc Michel     .write = cprman_write,
612fc14176bSLuc Michel     .endianness = DEVICE_LITTLE_ENDIAN,
613fc14176bSLuc Michel     .valid = {
614fc14176bSLuc Michel         /*
615fc14176bSLuc Michel          * Although this hasn't been checked against real hardware, nor the
616fc14176bSLuc Michel          * information can be found in a datasheet, it seems reasonable because
617fc14176bSLuc Michel          * of the "PASSWORD" magic value found in every registers.
618fc14176bSLuc Michel          */
619fc14176bSLuc Michel         .min_access_size        = 4,
620fc14176bSLuc Michel         .max_access_size        = 4,
621fc14176bSLuc Michel         .unaligned              = false,
622fc14176bSLuc Michel     },
623fc14176bSLuc Michel     .impl = {
624fc14176bSLuc Michel         .max_access_size = 4,
625fc14176bSLuc Michel     },
626fc14176bSLuc Michel };
627fc14176bSLuc Michel 
cprman_reset(DeviceState * dev)628fc14176bSLuc Michel static void cprman_reset(DeviceState *dev)
629fc14176bSLuc Michel {
630fc14176bSLuc Michel     BCM2835CprmanState *s = CPRMAN(dev);
6311e986e25SLuc Michel     size_t i;
632fc14176bSLuc Michel 
633fc14176bSLuc Michel     memset(s->regs, 0, sizeof(s->regs));
634fc14176bSLuc Michel 
6351e986e25SLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
6361e986e25SLuc Michel         device_cold_reset(DEVICE(&s->plls[i]));
6371e986e25SLuc Michel     }
6381e986e25SLuc Michel 
63909d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
64009d56bbcSLuc Michel         device_cold_reset(DEVICE(&s->channels[i]));
64109d56bbcSLuc Michel     }
64209d56bbcSLuc Michel 
643502960caSLuc Michel     device_cold_reset(DEVICE(&s->dsi0hsck_mux));
644502960caSLuc Michel 
64572813624SLuc Michel     for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) {
64672813624SLuc Michel         device_cold_reset(DEVICE(&s->clock_muxes[i]));
64772813624SLuc Michel     }
64872813624SLuc Michel 
649fc14176bSLuc Michel     clock_update_hz(s->xosc, s->xosc_freq);
650fc14176bSLuc Michel }
651fc14176bSLuc Michel 
cprman_init(Object * obj)652fc14176bSLuc Michel static void cprman_init(Object *obj)
653fc14176bSLuc Michel {
654fc14176bSLuc Michel     BCM2835CprmanState *s = CPRMAN(obj);
6551e986e25SLuc Michel     size_t i;
6561e986e25SLuc Michel 
6571e986e25SLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
6581e986e25SLuc Michel         object_initialize_child(obj, PLL_INIT_INFO[i].name,
6591e986e25SLuc Michel                                 &s->plls[i], TYPE_CPRMAN_PLL);
6601e986e25SLuc Michel         set_pll_init_info(s, &s->plls[i], i);
6611e986e25SLuc Michel     }
662fc14176bSLuc Michel 
66309d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
66409d56bbcSLuc Michel         object_initialize_child(obj, PLL_CHANNEL_INIT_INFO[i].name,
66509d56bbcSLuc Michel                                 &s->channels[i],
66609d56bbcSLuc Michel                                 TYPE_CPRMAN_PLL_CHANNEL);
66709d56bbcSLuc Michel         set_pll_channel_init_info(s, &s->channels[i], i);
66809d56bbcSLuc Michel     }
66909d56bbcSLuc Michel 
670502960caSLuc Michel     object_initialize_child(obj, "dsi0hsck-mux",
671502960caSLuc Michel                             &s->dsi0hsck_mux, TYPE_CPRMAN_DSI0HSCK_MUX);
672502960caSLuc Michel     s->dsi0hsck_mux.reg_cm = &s->regs[R_CM_DSI0HSCK];
673502960caSLuc Michel 
67472813624SLuc Michel     for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) {
67572813624SLuc Michel         char *alias;
67672813624SLuc Michel 
67772813624SLuc Michel         object_initialize_child(obj, CLOCK_MUX_INIT_INFO[i].name,
67872813624SLuc Michel                                 &s->clock_muxes[i],
67972813624SLuc Michel                                 TYPE_CPRMAN_CLOCK_MUX);
68072813624SLuc Michel         set_clock_mux_init_info(s, &s->clock_muxes[i], i);
68172813624SLuc Michel 
68272813624SLuc Michel         /* Expose muxes output as CPRMAN outputs */
68372813624SLuc Michel         alias = g_strdup_printf("%s-out", CLOCK_MUX_INIT_INFO[i].name);
68472813624SLuc Michel         qdev_alias_clock(DEVICE(&s->clock_muxes[i]), "out", DEVICE(obj), alias);
68572813624SLuc Michel         g_free(alias);
68672813624SLuc Michel     }
68772813624SLuc Michel 
688fc14176bSLuc Michel     s->xosc = clock_new(obj, "xosc");
68972813624SLuc Michel     s->gnd = clock_new(obj, "gnd");
69072813624SLuc Michel 
69172813624SLuc Michel     clock_set(s->gnd, 0);
692fc14176bSLuc Michel 
693fc14176bSLuc Michel     memory_region_init_io(&s->iomem, obj, &cprman_ops,
694fc14176bSLuc Michel                           s, "bcm2835-cprman", 0x2000);
695fc14176bSLuc Michel     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
696fc14176bSLuc Michel }
697fc14176bSLuc Michel 
connect_mux_sources(BCM2835CprmanState * s,CprmanClockMuxState * mux,const CprmanPllChannel * clk_mapping)69872813624SLuc Michel static void connect_mux_sources(BCM2835CprmanState *s,
69972813624SLuc Michel                                 CprmanClockMuxState *mux,
70072813624SLuc Michel                                 const CprmanPllChannel *clk_mapping)
70172813624SLuc Michel {
70272813624SLuc Michel     size_t i;
70372813624SLuc Michel     Clock *td0 = s->clock_muxes[CPRMAN_CLOCK_TD0].out;
70472813624SLuc Michel     Clock *td1 = s->clock_muxes[CPRMAN_CLOCK_TD1].out;
70572813624SLuc Michel 
70672813624SLuc Michel     /* For sources from 0 to 3. Source 4 to 9 are mux specific */
70772813624SLuc Michel     Clock * const CLK_SRC_MAPPING[] = {
70872813624SLuc Michel         [CPRMAN_CLOCK_SRC_GND] = s->gnd,
70972813624SLuc Michel         [CPRMAN_CLOCK_SRC_XOSC] = s->xosc,
71072813624SLuc Michel         [CPRMAN_CLOCK_SRC_TD0] = td0,
71172813624SLuc Michel         [CPRMAN_CLOCK_SRC_TD1] = td1,
71272813624SLuc Michel     };
71372813624SLuc Michel 
71472813624SLuc Michel     for (i = 0; i < CPRMAN_NUM_CLOCK_MUX_SRC; i++) {
71572813624SLuc Michel         CprmanPllChannel mapping = clk_mapping[i];
71672813624SLuc Michel         Clock *src;
71772813624SLuc Michel 
71872813624SLuc Michel         if (mapping == CPRMAN_CLOCK_SRC_FORCE_GROUND) {
71972813624SLuc Michel             src = s->gnd;
72072813624SLuc Michel         } else if (mapping == CPRMAN_CLOCK_SRC_DSI0HSCK) {
721502960caSLuc Michel             src = s->dsi0hsck_mux.out;
72272813624SLuc Michel         } else if (i < CPRMAN_CLOCK_SRC_PLLA) {
72372813624SLuc Michel             src = CLK_SRC_MAPPING[i];
72472813624SLuc Michel         } else {
72572813624SLuc Michel             src = s->channels[mapping].out;
72672813624SLuc Michel         }
72772813624SLuc Michel 
72872813624SLuc Michel         clock_set_source(mux->srcs[i], src);
72972813624SLuc Michel     }
73072813624SLuc Michel }
73172813624SLuc Michel 
cprman_realize(DeviceState * dev,Error ** errp)7321e986e25SLuc Michel static void cprman_realize(DeviceState *dev, Error **errp)
7331e986e25SLuc Michel {
7341e986e25SLuc Michel     BCM2835CprmanState *s = CPRMAN(dev);
7351e986e25SLuc Michel     size_t i;
7361e986e25SLuc Michel 
7371e986e25SLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
7381e986e25SLuc Michel         CprmanPllState *pll = &s->plls[i];
7391e986e25SLuc Michel 
7401e986e25SLuc Michel         clock_set_source(pll->xosc_in, s->xosc);
7411e986e25SLuc Michel 
7421e986e25SLuc Michel         if (!qdev_realize(DEVICE(pll), NULL, errp)) {
7431e986e25SLuc Michel             return;
7441e986e25SLuc Michel         }
7451e986e25SLuc Michel     }
74609d56bbcSLuc Michel 
74709d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
74809d56bbcSLuc Michel         CprmanPllChannelState *channel = &s->channels[i];
74909d56bbcSLuc Michel         CprmanPll parent = PLL_CHANNEL_INIT_INFO[i].parent;
75009d56bbcSLuc Michel         Clock *parent_clk = s->plls[parent].out;
75109d56bbcSLuc Michel 
75209d56bbcSLuc Michel         clock_set_source(channel->pll_in, parent_clk);
75309d56bbcSLuc Michel 
75409d56bbcSLuc Michel         if (!qdev_realize(DEVICE(channel), NULL, errp)) {
75509d56bbcSLuc Michel             return;
75609d56bbcSLuc Michel         }
75709d56bbcSLuc Michel     }
75872813624SLuc Michel 
759502960caSLuc Michel     clock_set_source(s->dsi0hsck_mux.plla_in,
760502960caSLuc Michel                      s->channels[CPRMAN_PLLA_CHANNEL_DSI0].out);
761502960caSLuc Michel     clock_set_source(s->dsi0hsck_mux.plld_in,
762502960caSLuc Michel                      s->channels[CPRMAN_PLLD_CHANNEL_DSI0].out);
763502960caSLuc Michel 
764502960caSLuc Michel     if (!qdev_realize(DEVICE(&s->dsi0hsck_mux), NULL, errp)) {
765502960caSLuc Michel         return;
766502960caSLuc Michel     }
767502960caSLuc Michel 
76872813624SLuc Michel     for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) {
76972813624SLuc Michel         CprmanClockMuxState *clock_mux = &s->clock_muxes[i];
77072813624SLuc Michel 
77172813624SLuc Michel         connect_mux_sources(s, clock_mux, CLOCK_MUX_INIT_INFO[i].src_mapping);
77272813624SLuc Michel 
77372813624SLuc Michel         if (!qdev_realize(DEVICE(clock_mux), NULL, errp)) {
77472813624SLuc Michel             return;
77572813624SLuc Michel         }
77672813624SLuc Michel     }
7771e986e25SLuc Michel }
7781e986e25SLuc Michel 
779fc14176bSLuc Michel static const VMStateDescription cprman_vmstate = {
780fc14176bSLuc Michel     .name = TYPE_BCM2835_CPRMAN,
781fc14176bSLuc Michel     .version_id = 1,
782fc14176bSLuc Michel     .minimum_version_id = 1,
783e4ea952fSRichard Henderson     .fields = (const VMStateField[]) {
784fc14176bSLuc Michel         VMSTATE_UINT32_ARRAY(regs, BCM2835CprmanState, CPRMAN_NUM_REGS),
785fc14176bSLuc Michel         VMSTATE_END_OF_LIST()
786fc14176bSLuc Michel     }
787fc14176bSLuc Michel };
788fc14176bSLuc Michel 
78930029973SRichard Henderson static const Property cprman_properties[] = {
790fc14176bSLuc Michel     DEFINE_PROP_UINT32("xosc-freq-hz", BCM2835CprmanState, xosc_freq, 19200000),
791fc14176bSLuc Michel };
792fc14176bSLuc Michel 
cprman_class_init(ObjectClass * klass,const void * data)793*12d1a768SPhilippe Mathieu-Daudé static void cprman_class_init(ObjectClass *klass, const void *data)
794fc14176bSLuc Michel {
795fc14176bSLuc Michel     DeviceClass *dc = DEVICE_CLASS(klass);
796fc14176bSLuc Michel 
7971e986e25SLuc Michel     dc->realize = cprman_realize;
798e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, cprman_reset);
799fc14176bSLuc Michel     dc->vmsd = &cprman_vmstate;
800fc14176bSLuc Michel     device_class_set_props(dc, cprman_properties);
801fc14176bSLuc Michel }
802fc14176bSLuc Michel 
803fc14176bSLuc Michel static const TypeInfo cprman_info = {
804fc14176bSLuc Michel     .name = TYPE_BCM2835_CPRMAN,
805fc14176bSLuc Michel     .parent = TYPE_SYS_BUS_DEVICE,
806fc14176bSLuc Michel     .instance_size = sizeof(BCM2835CprmanState),
807fc14176bSLuc Michel     .class_init = cprman_class_init,
808fc14176bSLuc Michel     .instance_init = cprman_init,
809fc14176bSLuc Michel };
810fc14176bSLuc Michel 
cprman_register_types(void)811fc14176bSLuc Michel static void cprman_register_types(void)
812fc14176bSLuc Michel {
813fc14176bSLuc Michel     type_register_static(&cprman_info);
8141e986e25SLuc Michel     type_register_static(&cprman_pll_info);
81509d56bbcSLuc Michel     type_register_static(&cprman_pll_channel_info);
81672813624SLuc Michel     type_register_static(&cprman_clock_mux_info);
817502960caSLuc Michel     type_register_static(&cprman_dsi0hsck_mux_info);
818fc14176bSLuc Michel }
819fc14176bSLuc Michel 
820fc14176bSLuc Michel type_init(cprman_register_types);
821