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