diff options
author | Luc Michel <luc@lmichel.fr> | 2020-10-10 15:57:50 +0200 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2020-10-27 11:10:44 +0000 |
commit | 1e986e25d03c0d579843c4d3e2915b2f4ac8e47f (patch) | |
tree | 33dbff2271cbb2637316ef1e486dcb5eaa86729b /hw/misc | |
parent | fc14176ba23de1386d8172d86a8006d9f8a555fc (diff) | |
download | qemu-1e986e25d03c0d579843c4d3e2915b2f4ac8e47f.zip qemu-1e986e25d03c0d579843c4d3e2915b2f4ac8e47f.tar.gz qemu-1e986e25d03c0d579843c4d3e2915b2f4ac8e47f.tar.bz2 |
hw/misc/bcm2835_cprman: add a PLL skeleton implementation
There are 5 PLLs in the CPRMAN, namely PLL A, C, D, H and B. All of them
take the xosc clock as input and produce a new clock.
This commit adds a skeleton implementation for the PLLs as sub-devices
of the CPRMAN. The PLLs are instantiated and connected internally to the
main oscillator.
Each PLL has 6 registers : CM, A2W_CTRL, A2W_ANA[0,1,2,3], A2W_FRAC. A
write to any of them triggers a call to the (not yet implemented)
pll_update function.
If the main oscillator changes frequency, an update is also triggered.
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Luc Michel <luc@lmichel.fr>
Tested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/misc')
-rw-r--r-- | hw/misc/bcm2835_cprman.c | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c index 57ab991..b86f590 100644 --- a/hw/misc/bcm2835_cprman.c +++ b/hw/misc/bcm2835_cprman.c @@ -48,6 +48,52 @@ #include "hw/misc/bcm2835_cprman_internals.h" #include "trace.h" +/* PLL */ + +static void pll_update(CprmanPllState *pll) +{ + clock_update(pll->out, 0); +} + +static void pll_xosc_update(void *opaque) +{ + pll_update(CPRMAN_PLL(opaque)); +} + +static void pll_init(Object *obj) +{ + CprmanPllState *s = CPRMAN_PLL(obj); + + s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update, s); + s->out = qdev_init_clock_out(DEVICE(s), "out"); +} + +static const VMStateDescription pll_vmstate = { + .name = TYPE_CPRMAN_PLL, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_CLOCK(xosc_in, CprmanPllState), + VMSTATE_END_OF_LIST() + } +}; + +static void pll_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &pll_vmstate; +} + +static const TypeInfo cprman_pll_info = { + .name = TYPE_CPRMAN_PLL, + .parent = TYPE_DEVICE, + .instance_size = sizeof(CprmanPllState), + .class_init = pll_class_init, + .instance_init = pll_init, +}; + + /* CPRMAN "top level" model */ static uint64_t cprman_read(void *opaque, hwaddr offset, @@ -66,6 +112,15 @@ static uint64_t cprman_read(void *opaque, hwaddr offset, return r; } +#define CASE_PLL_REGS(pll_) \ + case R_CM_ ## pll_: \ + case R_A2W_ ## pll_ ## _CTRL: \ + case R_A2W_ ## pll_ ## _ANA0: \ + case R_A2W_ ## pll_ ## _ANA1: \ + case R_A2W_ ## pll_ ## _ANA2: \ + case R_A2W_ ## pll_ ## _ANA3: \ + case R_A2W_ ## pll_ ## _FRAC + static void cprman_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { @@ -82,8 +137,31 @@ static void cprman_write(void *opaque, hwaddr offset, trace_bcm2835_cprman_write(offset, value); s->regs[idx] = value; + switch (idx) { + CASE_PLL_REGS(PLLA) : + pll_update(&s->plls[CPRMAN_PLLA]); + break; + + CASE_PLL_REGS(PLLC) : + pll_update(&s->plls[CPRMAN_PLLC]); + break; + + CASE_PLL_REGS(PLLD) : + pll_update(&s->plls[CPRMAN_PLLD]); + break; + + CASE_PLL_REGS(PLLH) : + pll_update(&s->plls[CPRMAN_PLLH]); + break; + + CASE_PLL_REGS(PLLB) : + pll_update(&s->plls[CPRMAN_PLLB]); + break; + } } +#undef CASE_PLL_REGS + static const MemoryRegionOps cprman_ops = { .read = cprman_read, .write = cprman_write, @@ -106,15 +184,27 @@ static const MemoryRegionOps cprman_ops = { static void cprman_reset(DeviceState *dev) { BCM2835CprmanState *s = CPRMAN(dev); + size_t i; memset(s->regs, 0, sizeof(s->regs)); + for (i = 0; i < CPRMAN_NUM_PLL; i++) { + device_cold_reset(DEVICE(&s->plls[i])); + } + clock_update_hz(s->xosc, s->xosc_freq); } static void cprman_init(Object *obj) { BCM2835CprmanState *s = CPRMAN(obj); + size_t i; + + for (i = 0; i < CPRMAN_NUM_PLL; i++) { + object_initialize_child(obj, PLL_INIT_INFO[i].name, + &s->plls[i], TYPE_CPRMAN_PLL); + set_pll_init_info(s, &s->plls[i], i); + } s->xosc = clock_new(obj, "xosc"); @@ -123,6 +213,22 @@ static void cprman_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); } +static void cprman_realize(DeviceState *dev, Error **errp) +{ + BCM2835CprmanState *s = CPRMAN(dev); + size_t i; + + for (i = 0; i < CPRMAN_NUM_PLL; i++) { + CprmanPllState *pll = &s->plls[i]; + + clock_set_source(pll->xosc_in, s->xosc); + + if (!qdev_realize(DEVICE(pll), NULL, errp)) { + return; + } + } +} + static const VMStateDescription cprman_vmstate = { .name = TYPE_BCM2835_CPRMAN, .version_id = 1, @@ -142,6 +248,7 @@ static void cprman_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = cprman_realize; dc->reset = cprman_reset; dc->vmsd = &cprman_vmstate; device_class_set_props(dc, cprman_properties); @@ -158,6 +265,7 @@ static const TypeInfo cprman_info = { static void cprman_register_types(void) { type_register_static(&cprman_info); + type_register_static(&cprman_pll_info); } type_init(cprman_register_types); |