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