xref: /qemu/hw/misc/bcm2835_cprman.c (revision 7281362484ac1c1bc854ca17291c4078e870eec2)
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.
41*72813624SLuc Michel  *
42*72813624SLuc Michel  * The CPRMAN exposes clock outputs with the name of the clock mux suffixed
43*72813624SLuc 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 
566d2b874cSLuc Michel static bool pll_is_locked(const CprmanPllState *pll)
576d2b874cSLuc Michel {
586d2b874cSLuc Michel     return !FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PWRDN)
596d2b874cSLuc Michel         && !FIELD_EX32(*pll->reg_cm, CM_PLLx, ANARST);
606d2b874cSLuc Michel }
616d2b874cSLuc Michel 
621e986e25SLuc Michel static void pll_update(CprmanPllState *pll)
631e986e25SLuc Michel {
646d2b874cSLuc Michel     uint64_t freq, ndiv, fdiv, pdiv;
656d2b874cSLuc Michel 
666d2b874cSLuc Michel     if (!pll_is_locked(pll)) {
671e986e25SLuc Michel         clock_update(pll->out, 0);
686d2b874cSLuc Michel         return;
696d2b874cSLuc Michel     }
706d2b874cSLuc Michel 
716d2b874cSLuc Michel     pdiv = FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PDIV);
726d2b874cSLuc Michel 
736d2b874cSLuc Michel     if (!pdiv) {
746d2b874cSLuc Michel         clock_update(pll->out, 0);
756d2b874cSLuc Michel         return;
766d2b874cSLuc Michel     }
776d2b874cSLuc Michel 
786d2b874cSLuc Michel     ndiv = FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, NDIV);
796d2b874cSLuc Michel     fdiv = FIELD_EX32(*pll->reg_a2w_frac, A2W_PLLx_FRAC, FRAC);
806d2b874cSLuc Michel 
816d2b874cSLuc Michel     if (pll->reg_a2w_ana[1] & pll->prediv_mask) {
826d2b874cSLuc Michel         /* The prescaler doubles the parent frequency */
836d2b874cSLuc Michel         ndiv *= 2;
846d2b874cSLuc Michel         fdiv *= 2;
856d2b874cSLuc Michel     }
866d2b874cSLuc Michel 
876d2b874cSLuc Michel     /*
886d2b874cSLuc Michel      * We have a multiplier with an integer part (ndiv) and a fractional part
896d2b874cSLuc Michel      * (fdiv), and a divider (pdiv).
906d2b874cSLuc Michel      */
916d2b874cSLuc Michel     freq = clock_get_hz(pll->xosc_in) *
926d2b874cSLuc Michel         ((ndiv << R_A2W_PLLx_FRAC_FRAC_LENGTH) + fdiv);
936d2b874cSLuc Michel     freq /= pdiv;
946d2b874cSLuc Michel     freq >>= R_A2W_PLLx_FRAC_FRAC_LENGTH;
956d2b874cSLuc Michel 
966d2b874cSLuc Michel     clock_update_hz(pll->out, freq);
971e986e25SLuc Michel }
981e986e25SLuc Michel 
991e986e25SLuc Michel static void pll_xosc_update(void *opaque)
1001e986e25SLuc Michel {
1011e986e25SLuc Michel     pll_update(CPRMAN_PLL(opaque));
1021e986e25SLuc Michel }
1031e986e25SLuc Michel 
1041e986e25SLuc Michel static void pll_init(Object *obj)
1051e986e25SLuc Michel {
1061e986e25SLuc Michel     CprmanPllState *s = CPRMAN_PLL(obj);
1071e986e25SLuc Michel 
1081e986e25SLuc Michel     s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update, s);
1091e986e25SLuc Michel     s->out = qdev_init_clock_out(DEVICE(s), "out");
1101e986e25SLuc Michel }
1111e986e25SLuc Michel 
1121e986e25SLuc Michel static const VMStateDescription pll_vmstate = {
1131e986e25SLuc Michel     .name = TYPE_CPRMAN_PLL,
1141e986e25SLuc Michel     .version_id = 1,
1151e986e25SLuc Michel     .minimum_version_id = 1,
1161e986e25SLuc Michel     .fields = (VMStateField[]) {
1171e986e25SLuc Michel         VMSTATE_CLOCK(xosc_in, CprmanPllState),
1181e986e25SLuc Michel         VMSTATE_END_OF_LIST()
1191e986e25SLuc Michel     }
1201e986e25SLuc Michel };
1211e986e25SLuc Michel 
1221e986e25SLuc Michel static void pll_class_init(ObjectClass *klass, void *data)
1231e986e25SLuc Michel {
1241e986e25SLuc Michel     DeviceClass *dc = DEVICE_CLASS(klass);
1251e986e25SLuc Michel 
1261e986e25SLuc Michel     dc->vmsd = &pll_vmstate;
1271e986e25SLuc Michel }
1281e986e25SLuc Michel 
1291e986e25SLuc Michel static const TypeInfo cprman_pll_info = {
1301e986e25SLuc Michel     .name = TYPE_CPRMAN_PLL,
1311e986e25SLuc Michel     .parent = TYPE_DEVICE,
1321e986e25SLuc Michel     .instance_size = sizeof(CprmanPllState),
1331e986e25SLuc Michel     .class_init = pll_class_init,
1341e986e25SLuc Michel     .instance_init = pll_init,
1351e986e25SLuc Michel };
1361e986e25SLuc Michel 
1371e986e25SLuc Michel 
13809d56bbcSLuc Michel /* PLL channel */
13909d56bbcSLuc Michel 
14095745811SLuc Michel static bool pll_channel_is_enabled(CprmanPllChannelState *channel)
14195745811SLuc Michel {
14295745811SLuc Michel     /*
14395745811SLuc Michel      * XXX I'm not sure of the purpose of the LOAD field. The Linux driver does
14495745811SLuc Michel      * not set it when enabling the channel, but does clear it when disabling
14595745811SLuc Michel      * it.
14695745811SLuc Michel      */
14795745811SLuc Michel     return !FIELD_EX32(*channel->reg_a2w_ctrl, A2W_PLLx_CHANNELy, DISABLE)
14895745811SLuc Michel         && !(*channel->reg_cm & channel->hold_mask);
14995745811SLuc Michel }
15095745811SLuc Michel 
15109d56bbcSLuc Michel static void pll_channel_update(CprmanPllChannelState *channel)
15209d56bbcSLuc Michel {
15395745811SLuc Michel     uint64_t freq, div;
15495745811SLuc Michel 
15595745811SLuc Michel     if (!pll_channel_is_enabled(channel)) {
15609d56bbcSLuc Michel         clock_update(channel->out, 0);
15795745811SLuc Michel         return;
15895745811SLuc Michel     }
15995745811SLuc Michel 
16095745811SLuc Michel     div = FIELD_EX32(*channel->reg_a2w_ctrl, A2W_PLLx_CHANNELy, DIV);
16195745811SLuc Michel 
16295745811SLuc Michel     if (!div) {
16395745811SLuc Michel         /*
16495745811SLuc Michel          * It seems that when the divider value is 0, it is considered as
16595745811SLuc Michel          * being maximum by the hardware (see the Linux driver).
16695745811SLuc Michel          */
16795745811SLuc Michel         div = R_A2W_PLLx_CHANNELy_DIV_MASK;
16895745811SLuc Michel     }
16995745811SLuc Michel 
17095745811SLuc Michel     /* Some channels have an additional fixed divider */
17195745811SLuc Michel     freq = clock_get_hz(channel->pll_in) / (div * channel->fixed_divider);
17295745811SLuc Michel 
17395745811SLuc Michel     clock_update_hz(channel->out, freq);
17409d56bbcSLuc Michel }
17509d56bbcSLuc Michel 
17609d56bbcSLuc Michel /* Update a PLL and all its channels */
17709d56bbcSLuc Michel static void pll_update_all_channels(BCM2835CprmanState *s,
17809d56bbcSLuc Michel                                     CprmanPllState *pll)
17909d56bbcSLuc Michel {
18009d56bbcSLuc Michel     size_t i;
18109d56bbcSLuc Michel 
18209d56bbcSLuc Michel     pll_update(pll);
18309d56bbcSLuc Michel 
18409d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
18509d56bbcSLuc Michel         CprmanPllChannelState *channel = &s->channels[i];
18609d56bbcSLuc Michel         if (channel->parent == pll->id) {
18709d56bbcSLuc Michel             pll_channel_update(channel);
18809d56bbcSLuc Michel         }
18909d56bbcSLuc Michel     }
19009d56bbcSLuc Michel }
19109d56bbcSLuc Michel 
19209d56bbcSLuc Michel static void pll_channel_pll_in_update(void *opaque)
19309d56bbcSLuc Michel {
19409d56bbcSLuc Michel     pll_channel_update(CPRMAN_PLL_CHANNEL(opaque));
19509d56bbcSLuc Michel }
19609d56bbcSLuc Michel 
19709d56bbcSLuc Michel static void pll_channel_init(Object *obj)
19809d56bbcSLuc Michel {
19909d56bbcSLuc Michel     CprmanPllChannelState *s = CPRMAN_PLL_CHANNEL(obj);
20009d56bbcSLuc Michel 
20109d56bbcSLuc Michel     s->pll_in = qdev_init_clock_in(DEVICE(s), "pll-in",
20209d56bbcSLuc Michel                                    pll_channel_pll_in_update, s);
20309d56bbcSLuc Michel     s->out = qdev_init_clock_out(DEVICE(s), "out");
20409d56bbcSLuc Michel }
20509d56bbcSLuc Michel 
20609d56bbcSLuc Michel static const VMStateDescription pll_channel_vmstate = {
20709d56bbcSLuc Michel     .name = TYPE_CPRMAN_PLL_CHANNEL,
20809d56bbcSLuc Michel     .version_id = 1,
20909d56bbcSLuc Michel     .minimum_version_id = 1,
21009d56bbcSLuc Michel     .fields = (VMStateField[]) {
21109d56bbcSLuc Michel         VMSTATE_CLOCK(pll_in, CprmanPllChannelState),
21209d56bbcSLuc Michel         VMSTATE_END_OF_LIST()
21309d56bbcSLuc Michel     }
21409d56bbcSLuc Michel };
21509d56bbcSLuc Michel 
21609d56bbcSLuc Michel static void pll_channel_class_init(ObjectClass *klass, void *data)
21709d56bbcSLuc Michel {
21809d56bbcSLuc Michel     DeviceClass *dc = DEVICE_CLASS(klass);
21909d56bbcSLuc Michel 
22009d56bbcSLuc Michel     dc->vmsd = &pll_channel_vmstate;
22109d56bbcSLuc Michel }
22209d56bbcSLuc Michel 
22309d56bbcSLuc Michel static const TypeInfo cprman_pll_channel_info = {
22409d56bbcSLuc Michel     .name = TYPE_CPRMAN_PLL_CHANNEL,
22509d56bbcSLuc Michel     .parent = TYPE_DEVICE,
22609d56bbcSLuc Michel     .instance_size = sizeof(CprmanPllChannelState),
22709d56bbcSLuc Michel     .class_init = pll_channel_class_init,
22809d56bbcSLuc Michel     .instance_init = pll_channel_init,
22909d56bbcSLuc Michel };
23009d56bbcSLuc Michel 
23109d56bbcSLuc Michel 
232*72813624SLuc Michel /* clock mux */
233*72813624SLuc Michel 
234*72813624SLuc Michel static void clock_mux_update(CprmanClockMuxState *mux)
235*72813624SLuc Michel {
236*72813624SLuc Michel     clock_update(mux->out, 0);
237*72813624SLuc Michel }
238*72813624SLuc Michel 
239*72813624SLuc Michel static void clock_mux_src_update(void *opaque)
240*72813624SLuc Michel {
241*72813624SLuc Michel     CprmanClockMuxState **backref = opaque;
242*72813624SLuc Michel     CprmanClockMuxState *s = *backref;
243*72813624SLuc Michel 
244*72813624SLuc Michel     clock_mux_update(s);
245*72813624SLuc Michel }
246*72813624SLuc Michel 
247*72813624SLuc Michel static void clock_mux_init(Object *obj)
248*72813624SLuc Michel {
249*72813624SLuc Michel     CprmanClockMuxState *s = CPRMAN_CLOCK_MUX(obj);
250*72813624SLuc Michel     size_t i;
251*72813624SLuc Michel 
252*72813624SLuc Michel     for (i = 0; i < CPRMAN_NUM_CLOCK_MUX_SRC; i++) {
253*72813624SLuc Michel         char *name = g_strdup_printf("srcs[%zu]", i);
254*72813624SLuc Michel         s->backref[i] = s;
255*72813624SLuc Michel         s->srcs[i] = qdev_init_clock_in(DEVICE(s), name,
256*72813624SLuc Michel                                         clock_mux_src_update,
257*72813624SLuc Michel                                         &s->backref[i]);
258*72813624SLuc Michel         g_free(name);
259*72813624SLuc Michel     }
260*72813624SLuc Michel 
261*72813624SLuc Michel     s->out = qdev_init_clock_out(DEVICE(s), "out");
262*72813624SLuc Michel }
263*72813624SLuc Michel 
264*72813624SLuc Michel static const VMStateDescription clock_mux_vmstate = {
265*72813624SLuc Michel     .name = TYPE_CPRMAN_CLOCK_MUX,
266*72813624SLuc Michel     .version_id = 1,
267*72813624SLuc Michel     .minimum_version_id = 1,
268*72813624SLuc Michel     .fields = (VMStateField[]) {
269*72813624SLuc Michel         VMSTATE_ARRAY_CLOCK(srcs, CprmanClockMuxState,
270*72813624SLuc Michel                             CPRMAN_NUM_CLOCK_MUX_SRC),
271*72813624SLuc Michel         VMSTATE_END_OF_LIST()
272*72813624SLuc Michel     }
273*72813624SLuc Michel };
274*72813624SLuc Michel 
275*72813624SLuc Michel static void clock_mux_class_init(ObjectClass *klass, void *data)
276*72813624SLuc Michel {
277*72813624SLuc Michel     DeviceClass *dc = DEVICE_CLASS(klass);
278*72813624SLuc Michel 
279*72813624SLuc Michel     dc->vmsd = &clock_mux_vmstate;
280*72813624SLuc Michel }
281*72813624SLuc Michel 
282*72813624SLuc Michel static const TypeInfo cprman_clock_mux_info = {
283*72813624SLuc Michel     .name = TYPE_CPRMAN_CLOCK_MUX,
284*72813624SLuc Michel     .parent = TYPE_DEVICE,
285*72813624SLuc Michel     .instance_size = sizeof(CprmanClockMuxState),
286*72813624SLuc Michel     .class_init = clock_mux_class_init,
287*72813624SLuc Michel     .instance_init = clock_mux_init,
288*72813624SLuc Michel };
289*72813624SLuc Michel 
290*72813624SLuc Michel 
291fc14176bSLuc Michel /* CPRMAN "top level" model */
292fc14176bSLuc Michel 
2936d2b874cSLuc Michel static uint32_t get_cm_lock(const BCM2835CprmanState *s)
2946d2b874cSLuc Michel {
2956d2b874cSLuc Michel     static const int CM_LOCK_MAPPING[CPRMAN_NUM_PLL] = {
2966d2b874cSLuc Michel         [CPRMAN_PLLA] = R_CM_LOCK_FLOCKA_SHIFT,
2976d2b874cSLuc Michel         [CPRMAN_PLLC] = R_CM_LOCK_FLOCKC_SHIFT,
2986d2b874cSLuc Michel         [CPRMAN_PLLD] = R_CM_LOCK_FLOCKD_SHIFT,
2996d2b874cSLuc Michel         [CPRMAN_PLLH] = R_CM_LOCK_FLOCKH_SHIFT,
3006d2b874cSLuc Michel         [CPRMAN_PLLB] = R_CM_LOCK_FLOCKB_SHIFT,
3016d2b874cSLuc Michel     };
3026d2b874cSLuc Michel 
3036d2b874cSLuc Michel     uint32_t r = 0;
3046d2b874cSLuc Michel     size_t i;
3056d2b874cSLuc Michel 
3066d2b874cSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
3076d2b874cSLuc Michel         r |= pll_is_locked(&s->plls[i]) << CM_LOCK_MAPPING[i];
3086d2b874cSLuc Michel     }
3096d2b874cSLuc Michel 
3106d2b874cSLuc Michel     return r;
3116d2b874cSLuc Michel }
3126d2b874cSLuc Michel 
313fc14176bSLuc Michel static uint64_t cprman_read(void *opaque, hwaddr offset,
314fc14176bSLuc Michel                             unsigned size)
315fc14176bSLuc Michel {
316fc14176bSLuc Michel     BCM2835CprmanState *s = CPRMAN(opaque);
317fc14176bSLuc Michel     uint64_t r = 0;
318fc14176bSLuc Michel     size_t idx = offset / sizeof(uint32_t);
319fc14176bSLuc Michel 
320fc14176bSLuc Michel     switch (idx) {
3216d2b874cSLuc Michel     case R_CM_LOCK:
3226d2b874cSLuc Michel         r = get_cm_lock(s);
3236d2b874cSLuc Michel         break;
3246d2b874cSLuc Michel 
325fc14176bSLuc Michel     default:
326fc14176bSLuc Michel         r = s->regs[idx];
327fc14176bSLuc Michel     }
328fc14176bSLuc Michel 
329fc14176bSLuc Michel     trace_bcm2835_cprman_read(offset, r);
330fc14176bSLuc Michel     return r;
331fc14176bSLuc Michel }
332fc14176bSLuc Michel 
33309d56bbcSLuc Michel static inline void update_pll_and_channels_from_cm(BCM2835CprmanState *s,
33409d56bbcSLuc Michel                                                    size_t idx)
33509d56bbcSLuc Michel {
33609d56bbcSLuc Michel     size_t i;
33709d56bbcSLuc Michel 
33809d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
33909d56bbcSLuc Michel         if (PLL_INIT_INFO[i].cm_offset == idx) {
34009d56bbcSLuc Michel             pll_update_all_channels(s, &s->plls[i]);
34109d56bbcSLuc Michel             return;
34209d56bbcSLuc Michel         }
34309d56bbcSLuc Michel     }
34409d56bbcSLuc Michel }
34509d56bbcSLuc Michel 
34609d56bbcSLuc Michel static inline void update_channel_from_a2w(BCM2835CprmanState *s, size_t idx)
34709d56bbcSLuc Michel {
34809d56bbcSLuc Michel     size_t i;
34909d56bbcSLuc Michel 
35009d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
35109d56bbcSLuc Michel         if (PLL_CHANNEL_INIT_INFO[i].a2w_ctrl_offset == idx) {
35209d56bbcSLuc Michel             pll_channel_update(&s->channels[i]);
35309d56bbcSLuc Michel             return;
35409d56bbcSLuc Michel         }
35509d56bbcSLuc Michel     }
35609d56bbcSLuc Michel }
35709d56bbcSLuc Michel 
358*72813624SLuc Michel static inline void update_mux_from_cm(BCM2835CprmanState *s, size_t idx)
359*72813624SLuc Michel {
360*72813624SLuc Michel     size_t i;
361*72813624SLuc Michel 
362*72813624SLuc Michel     for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) {
363*72813624SLuc Michel         if ((CLOCK_MUX_INIT_INFO[i].cm_offset == idx) ||
364*72813624SLuc Michel             (CLOCK_MUX_INIT_INFO[i].cm_offset + 4 == idx)) {
365*72813624SLuc Michel             /* matches CM_CTL or CM_DIV mux register */
366*72813624SLuc Michel             clock_mux_update(&s->clock_muxes[i]);
367*72813624SLuc Michel             return;
368*72813624SLuc Michel         }
369*72813624SLuc Michel     }
370*72813624SLuc Michel }
371*72813624SLuc Michel 
37209d56bbcSLuc Michel #define CASE_PLL_A2W_REGS(pll_) \
3731e986e25SLuc Michel     case R_A2W_ ## pll_ ## _CTRL: \
3741e986e25SLuc Michel     case R_A2W_ ## pll_ ## _ANA0: \
3751e986e25SLuc Michel     case R_A2W_ ## pll_ ## _ANA1: \
3761e986e25SLuc Michel     case R_A2W_ ## pll_ ## _ANA2: \
3771e986e25SLuc Michel     case R_A2W_ ## pll_ ## _ANA3: \
3781e986e25SLuc Michel     case R_A2W_ ## pll_ ## _FRAC
3791e986e25SLuc Michel 
380fc14176bSLuc Michel static void cprman_write(void *opaque, hwaddr offset,
381fc14176bSLuc Michel                          uint64_t value, unsigned size)
382fc14176bSLuc Michel {
383fc14176bSLuc Michel     BCM2835CprmanState *s = CPRMAN(opaque);
384fc14176bSLuc Michel     size_t idx = offset / sizeof(uint32_t);
385fc14176bSLuc Michel 
386fc14176bSLuc Michel     if (FIELD_EX32(value, CPRMAN, PASSWORD) != CPRMAN_PASSWORD) {
387fc14176bSLuc Michel         trace_bcm2835_cprman_write_invalid_magic(offset, value);
388fc14176bSLuc Michel         return;
389fc14176bSLuc Michel     }
390fc14176bSLuc Michel 
391fc14176bSLuc Michel     value &= ~R_CPRMAN_PASSWORD_MASK;
392fc14176bSLuc Michel 
393fc14176bSLuc Michel     trace_bcm2835_cprman_write(offset, value);
394fc14176bSLuc Michel     s->regs[idx] = value;
395fc14176bSLuc Michel 
3961e986e25SLuc Michel     switch (idx) {
39709d56bbcSLuc Michel     case R_CM_PLLA ... R_CM_PLLH:
39809d56bbcSLuc Michel     case R_CM_PLLB:
39909d56bbcSLuc Michel         /*
40009d56bbcSLuc Michel          * A given CM_PLLx register is shared by both the PLL and the channels
40109d56bbcSLuc Michel          * of this PLL.
40209d56bbcSLuc Michel          */
40309d56bbcSLuc Michel         update_pll_and_channels_from_cm(s, idx);
40409d56bbcSLuc Michel         break;
40509d56bbcSLuc Michel 
40609d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLA) :
4071e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLA]);
4081e986e25SLuc Michel         break;
4091e986e25SLuc Michel 
41009d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLC) :
4111e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLC]);
4121e986e25SLuc Michel         break;
4131e986e25SLuc Michel 
41409d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLD) :
4151e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLD]);
4161e986e25SLuc Michel         break;
4171e986e25SLuc Michel 
41809d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLH) :
4191e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLH]);
4201e986e25SLuc Michel         break;
4211e986e25SLuc Michel 
42209d56bbcSLuc Michel     CASE_PLL_A2W_REGS(PLLB) :
4231e986e25SLuc Michel         pll_update(&s->plls[CPRMAN_PLLB]);
4241e986e25SLuc Michel         break;
42509d56bbcSLuc Michel 
42609d56bbcSLuc Michel     case R_A2W_PLLA_DSI0:
42709d56bbcSLuc Michel     case R_A2W_PLLA_CORE:
42809d56bbcSLuc Michel     case R_A2W_PLLA_PER:
42909d56bbcSLuc Michel     case R_A2W_PLLA_CCP2:
43009d56bbcSLuc Michel     case R_A2W_PLLC_CORE2:
43109d56bbcSLuc Michel     case R_A2W_PLLC_CORE1:
43209d56bbcSLuc Michel     case R_A2W_PLLC_PER:
43309d56bbcSLuc Michel     case R_A2W_PLLC_CORE0:
43409d56bbcSLuc Michel     case R_A2W_PLLD_DSI0:
43509d56bbcSLuc Michel     case R_A2W_PLLD_CORE:
43609d56bbcSLuc Michel     case R_A2W_PLLD_PER:
43709d56bbcSLuc Michel     case R_A2W_PLLD_DSI1:
43809d56bbcSLuc Michel     case R_A2W_PLLH_AUX:
43909d56bbcSLuc Michel     case R_A2W_PLLH_RCAL:
44009d56bbcSLuc Michel     case R_A2W_PLLH_PIX:
44109d56bbcSLuc Michel     case R_A2W_PLLB_ARM:
44209d56bbcSLuc Michel         update_channel_from_a2w(s, idx);
44309d56bbcSLuc Michel         break;
444*72813624SLuc Michel 
445*72813624SLuc Michel     case R_CM_GNRICCTL ... R_CM_SMIDIV:
446*72813624SLuc Michel     case R_CM_TCNTCNT ... R_CM_VECDIV:
447*72813624SLuc Michel     case R_CM_PULSECTL ... R_CM_PULSEDIV:
448*72813624SLuc Michel     case R_CM_SDCCTL ... R_CM_ARMCTL:
449*72813624SLuc Michel     case R_CM_AVEOCTL ... R_CM_EMMCDIV:
450*72813624SLuc Michel     case R_CM_EMMC2CTL ... R_CM_EMMC2DIV:
451*72813624SLuc Michel         update_mux_from_cm(s, idx);
452*72813624SLuc Michel         break;
453fc14176bSLuc Michel     }
4541e986e25SLuc Michel }
4551e986e25SLuc Michel 
45609d56bbcSLuc Michel #undef CASE_PLL_A2W_REGS
457fc14176bSLuc Michel 
458fc14176bSLuc Michel static const MemoryRegionOps cprman_ops = {
459fc14176bSLuc Michel     .read = cprman_read,
460fc14176bSLuc Michel     .write = cprman_write,
461fc14176bSLuc Michel     .endianness = DEVICE_LITTLE_ENDIAN,
462fc14176bSLuc Michel     .valid = {
463fc14176bSLuc Michel         /*
464fc14176bSLuc Michel          * Although this hasn't been checked against real hardware, nor the
465fc14176bSLuc Michel          * information can be found in a datasheet, it seems reasonable because
466fc14176bSLuc Michel          * of the "PASSWORD" magic value found in every registers.
467fc14176bSLuc Michel          */
468fc14176bSLuc Michel         .min_access_size        = 4,
469fc14176bSLuc Michel         .max_access_size        = 4,
470fc14176bSLuc Michel         .unaligned              = false,
471fc14176bSLuc Michel     },
472fc14176bSLuc Michel     .impl = {
473fc14176bSLuc Michel         .max_access_size = 4,
474fc14176bSLuc Michel     },
475fc14176bSLuc Michel };
476fc14176bSLuc Michel 
477fc14176bSLuc Michel static void cprman_reset(DeviceState *dev)
478fc14176bSLuc Michel {
479fc14176bSLuc Michel     BCM2835CprmanState *s = CPRMAN(dev);
4801e986e25SLuc Michel     size_t i;
481fc14176bSLuc Michel 
482fc14176bSLuc Michel     memset(s->regs, 0, sizeof(s->regs));
483fc14176bSLuc Michel 
4841e986e25SLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
4851e986e25SLuc Michel         device_cold_reset(DEVICE(&s->plls[i]));
4861e986e25SLuc Michel     }
4871e986e25SLuc Michel 
48809d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
48909d56bbcSLuc Michel         device_cold_reset(DEVICE(&s->channels[i]));
49009d56bbcSLuc Michel     }
49109d56bbcSLuc Michel 
492*72813624SLuc Michel     for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) {
493*72813624SLuc Michel         device_cold_reset(DEVICE(&s->clock_muxes[i]));
494*72813624SLuc Michel     }
495*72813624SLuc Michel 
496fc14176bSLuc Michel     clock_update_hz(s->xosc, s->xosc_freq);
497fc14176bSLuc Michel }
498fc14176bSLuc Michel 
499fc14176bSLuc Michel static void cprman_init(Object *obj)
500fc14176bSLuc Michel {
501fc14176bSLuc Michel     BCM2835CprmanState *s = CPRMAN(obj);
5021e986e25SLuc Michel     size_t i;
5031e986e25SLuc Michel 
5041e986e25SLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
5051e986e25SLuc Michel         object_initialize_child(obj, PLL_INIT_INFO[i].name,
5061e986e25SLuc Michel                                 &s->plls[i], TYPE_CPRMAN_PLL);
5071e986e25SLuc Michel         set_pll_init_info(s, &s->plls[i], i);
5081e986e25SLuc Michel     }
509fc14176bSLuc Michel 
51009d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
51109d56bbcSLuc Michel         object_initialize_child(obj, PLL_CHANNEL_INIT_INFO[i].name,
51209d56bbcSLuc Michel                                 &s->channels[i],
51309d56bbcSLuc Michel                                 TYPE_CPRMAN_PLL_CHANNEL);
51409d56bbcSLuc Michel         set_pll_channel_init_info(s, &s->channels[i], i);
51509d56bbcSLuc Michel     }
51609d56bbcSLuc Michel 
517*72813624SLuc Michel     for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) {
518*72813624SLuc Michel         char *alias;
519*72813624SLuc Michel 
520*72813624SLuc Michel         object_initialize_child(obj, CLOCK_MUX_INIT_INFO[i].name,
521*72813624SLuc Michel                                 &s->clock_muxes[i],
522*72813624SLuc Michel                                 TYPE_CPRMAN_CLOCK_MUX);
523*72813624SLuc Michel         set_clock_mux_init_info(s, &s->clock_muxes[i], i);
524*72813624SLuc Michel 
525*72813624SLuc Michel         /* Expose muxes output as CPRMAN outputs */
526*72813624SLuc Michel         alias = g_strdup_printf("%s-out", CLOCK_MUX_INIT_INFO[i].name);
527*72813624SLuc Michel         qdev_alias_clock(DEVICE(&s->clock_muxes[i]), "out", DEVICE(obj), alias);
528*72813624SLuc Michel         g_free(alias);
529*72813624SLuc Michel     }
530*72813624SLuc Michel 
531fc14176bSLuc Michel     s->xosc = clock_new(obj, "xosc");
532*72813624SLuc Michel     s->gnd = clock_new(obj, "gnd");
533*72813624SLuc Michel 
534*72813624SLuc Michel     clock_set(s->gnd, 0);
535fc14176bSLuc Michel 
536fc14176bSLuc Michel     memory_region_init_io(&s->iomem, obj, &cprman_ops,
537fc14176bSLuc Michel                           s, "bcm2835-cprman", 0x2000);
538fc14176bSLuc Michel     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
539fc14176bSLuc Michel }
540fc14176bSLuc Michel 
541*72813624SLuc Michel static void connect_mux_sources(BCM2835CprmanState *s,
542*72813624SLuc Michel                                 CprmanClockMuxState *mux,
543*72813624SLuc Michel                                 const CprmanPllChannel *clk_mapping)
544*72813624SLuc Michel {
545*72813624SLuc Michel     size_t i;
546*72813624SLuc Michel     Clock *td0 = s->clock_muxes[CPRMAN_CLOCK_TD0].out;
547*72813624SLuc Michel     Clock *td1 = s->clock_muxes[CPRMAN_CLOCK_TD1].out;
548*72813624SLuc Michel 
549*72813624SLuc Michel     /* For sources from 0 to 3. Source 4 to 9 are mux specific */
550*72813624SLuc Michel     Clock * const CLK_SRC_MAPPING[] = {
551*72813624SLuc Michel         [CPRMAN_CLOCK_SRC_GND] = s->gnd,
552*72813624SLuc Michel         [CPRMAN_CLOCK_SRC_XOSC] = s->xosc,
553*72813624SLuc Michel         [CPRMAN_CLOCK_SRC_TD0] = td0,
554*72813624SLuc Michel         [CPRMAN_CLOCK_SRC_TD1] = td1,
555*72813624SLuc Michel     };
556*72813624SLuc Michel 
557*72813624SLuc Michel     for (i = 0; i < CPRMAN_NUM_CLOCK_MUX_SRC; i++) {
558*72813624SLuc Michel         CprmanPllChannel mapping = clk_mapping[i];
559*72813624SLuc Michel         Clock *src;
560*72813624SLuc Michel 
561*72813624SLuc Michel         if (mapping == CPRMAN_CLOCK_SRC_FORCE_GROUND) {
562*72813624SLuc Michel             src = s->gnd;
563*72813624SLuc Michel         } else if (mapping == CPRMAN_CLOCK_SRC_DSI0HSCK) {
564*72813624SLuc Michel             src = s->gnd; /* TODO */
565*72813624SLuc Michel         } else if (i < CPRMAN_CLOCK_SRC_PLLA) {
566*72813624SLuc Michel             src = CLK_SRC_MAPPING[i];
567*72813624SLuc Michel         } else {
568*72813624SLuc Michel             src = s->channels[mapping].out;
569*72813624SLuc Michel         }
570*72813624SLuc Michel 
571*72813624SLuc Michel         clock_set_source(mux->srcs[i], src);
572*72813624SLuc Michel     }
573*72813624SLuc Michel }
574*72813624SLuc Michel 
5751e986e25SLuc Michel static void cprman_realize(DeviceState *dev, Error **errp)
5761e986e25SLuc Michel {
5771e986e25SLuc Michel     BCM2835CprmanState *s = CPRMAN(dev);
5781e986e25SLuc Michel     size_t i;
5791e986e25SLuc Michel 
5801e986e25SLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
5811e986e25SLuc Michel         CprmanPllState *pll = &s->plls[i];
5821e986e25SLuc Michel 
5831e986e25SLuc Michel         clock_set_source(pll->xosc_in, s->xosc);
5841e986e25SLuc Michel 
5851e986e25SLuc Michel         if (!qdev_realize(DEVICE(pll), NULL, errp)) {
5861e986e25SLuc Michel             return;
5871e986e25SLuc Michel         }
5881e986e25SLuc Michel     }
58909d56bbcSLuc Michel 
59009d56bbcSLuc Michel     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
59109d56bbcSLuc Michel         CprmanPllChannelState *channel = &s->channels[i];
59209d56bbcSLuc Michel         CprmanPll parent = PLL_CHANNEL_INIT_INFO[i].parent;
59309d56bbcSLuc Michel         Clock *parent_clk = s->plls[parent].out;
59409d56bbcSLuc Michel 
59509d56bbcSLuc Michel         clock_set_source(channel->pll_in, parent_clk);
59609d56bbcSLuc Michel 
59709d56bbcSLuc Michel         if (!qdev_realize(DEVICE(channel), NULL, errp)) {
59809d56bbcSLuc Michel             return;
59909d56bbcSLuc Michel         }
60009d56bbcSLuc Michel     }
601*72813624SLuc Michel 
602*72813624SLuc Michel     for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) {
603*72813624SLuc Michel         CprmanClockMuxState *clock_mux = &s->clock_muxes[i];
604*72813624SLuc Michel 
605*72813624SLuc Michel         connect_mux_sources(s, clock_mux, CLOCK_MUX_INIT_INFO[i].src_mapping);
606*72813624SLuc Michel 
607*72813624SLuc Michel         if (!qdev_realize(DEVICE(clock_mux), NULL, errp)) {
608*72813624SLuc Michel             return;
609*72813624SLuc Michel         }
610*72813624SLuc Michel     }
6111e986e25SLuc Michel }
6121e986e25SLuc Michel 
613fc14176bSLuc Michel static const VMStateDescription cprman_vmstate = {
614fc14176bSLuc Michel     .name = TYPE_BCM2835_CPRMAN,
615fc14176bSLuc Michel     .version_id = 1,
616fc14176bSLuc Michel     .minimum_version_id = 1,
617fc14176bSLuc Michel     .fields = (VMStateField[]) {
618fc14176bSLuc Michel         VMSTATE_UINT32_ARRAY(regs, BCM2835CprmanState, CPRMAN_NUM_REGS),
619fc14176bSLuc Michel         VMSTATE_END_OF_LIST()
620fc14176bSLuc Michel     }
621fc14176bSLuc Michel };
622fc14176bSLuc Michel 
623fc14176bSLuc Michel static Property cprman_properties[] = {
624fc14176bSLuc Michel     DEFINE_PROP_UINT32("xosc-freq-hz", BCM2835CprmanState, xosc_freq, 19200000),
625fc14176bSLuc Michel     DEFINE_PROP_END_OF_LIST()
626fc14176bSLuc Michel };
627fc14176bSLuc Michel 
628fc14176bSLuc Michel static void cprman_class_init(ObjectClass *klass, void *data)
629fc14176bSLuc Michel {
630fc14176bSLuc Michel     DeviceClass *dc = DEVICE_CLASS(klass);
631fc14176bSLuc Michel 
6321e986e25SLuc Michel     dc->realize = cprman_realize;
633fc14176bSLuc Michel     dc->reset = cprman_reset;
634fc14176bSLuc Michel     dc->vmsd = &cprman_vmstate;
635fc14176bSLuc Michel     device_class_set_props(dc, cprman_properties);
636fc14176bSLuc Michel }
637fc14176bSLuc Michel 
638fc14176bSLuc Michel static const TypeInfo cprman_info = {
639fc14176bSLuc Michel     .name = TYPE_BCM2835_CPRMAN,
640fc14176bSLuc Michel     .parent = TYPE_SYS_BUS_DEVICE,
641fc14176bSLuc Michel     .instance_size = sizeof(BCM2835CprmanState),
642fc14176bSLuc Michel     .class_init = cprman_class_init,
643fc14176bSLuc Michel     .instance_init = cprman_init,
644fc14176bSLuc Michel };
645fc14176bSLuc Michel 
646fc14176bSLuc Michel static void cprman_register_types(void)
647fc14176bSLuc Michel {
648fc14176bSLuc Michel     type_register_static(&cprman_info);
6491e986e25SLuc Michel     type_register_static(&cprman_pll_info);
65009d56bbcSLuc Michel     type_register_static(&cprman_pll_channel_info);
651*72813624SLuc Michel     type_register_static(&cprman_clock_mux_info);
652fc14176bSLuc Michel }
653fc14176bSLuc Michel 
654fc14176bSLuc Michel type_init(cprman_register_types);
655