stm32l4x5_rcc.c (d6b55a0fe9920b46d380f50d7da48ff43de21324) stm32l4x5_rcc.c (ec7d83acbd1182d47df742745b43e6b16a3a4977)
1/*
2 * STM32L4X5 RCC (Reset and clock control)
3 *
4 * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
5 * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr>
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 *

--- 22 unchanged lines hidden (view full) ---

31#include "trace.h"
32
33#define HSE_DEFAULT_FRQ 48000000ULL
34#define HSI_FRQ 16000000ULL
35#define MSI_DEFAULT_FRQ 4000000ULL
36#define LSE_FRQ 32768ULL
37#define LSI_FRQ 32000ULL
38
1/*
2 * STM32L4X5 RCC (Reset and clock control)
3 *
4 * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
5 * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr>
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 *

--- 22 unchanged lines hidden (view full) ---

31#include "trace.h"
32
33#define HSE_DEFAULT_FRQ 48000000ULL
34#define HSI_FRQ 16000000ULL
35#define MSI_DEFAULT_FRQ 4000000ULL
36#define LSE_FRQ 32768ULL
37#define LSI_FRQ 32000ULL
38
39static void clock_mux_update(RccClockMuxState *mux)
40{
41 uint64_t src_freq;
42 Clock *current_source = mux->srcs[mux->src];
43 uint32_t freq_multiplier = 0;
44 /*
45 * To avoid rounding errors, we use the clock period instead of the
46 * frequency.
47 * This means that the multiplier of the mux becomes the divider of
48 * the clock and the divider of the mux becomes the multiplier of the
49 * clock.
50 */
51 if (mux->enabled && mux->divider) {
52 freq_multiplier = mux->divider;
53 }
54
55 clock_set_mul_div(mux->out, freq_multiplier, mux->multiplier);
56 clock_update(mux->out, clock_get(current_source));
57
58 src_freq = clock_get_hz(current_source);
59 /* TODO: can we simply detect if the config changed so that we reduce log spam ? */
60 trace_stm32l4x5_rcc_mux_update(mux->id, mux->src, src_freq,
61 mux->multiplier, mux->divider);
62}
63
64static void clock_mux_src_update(void *opaque, ClockEvent event)
65{
66 RccClockMuxState **backref = opaque;
67 RccClockMuxState *s = *backref;
68 /*
69 * The backref value is equal to:
70 * s->backref + (sizeof(RccClockMuxState *) * update_src).
71 * By subtracting we can get back the index of the updated clock.
72 */
73 const uint32_t update_src = backref - s->backref;
74 /* Only update if the clock that was updated is the current source */
75 if (update_src == s->src) {
76 clock_mux_update(s);
77 }
78}
79
80static void clock_mux_init(Object *obj)
81{
82 RccClockMuxState *s = RCC_CLOCK_MUX(obj);
83 size_t i;
84
85 for (i = 0; i < RCC_NUM_CLOCK_MUX_SRC; i++) {
86 char *name = g_strdup_printf("srcs[%zu]", i);
87 s->backref[i] = s;
88 s->srcs[i] = qdev_init_clock_in(DEVICE(s), name,
89 clock_mux_src_update,
90 &s->backref[i],
91 ClockUpdate);
92 g_free(name);
93 }
94
95 s->out = qdev_init_clock_out(DEVICE(s), "out");
96}
97
98static void clock_mux_reset_hold(Object *obj)
99{ }
100
101static const VMStateDescription clock_mux_vmstate = {
102 .name = TYPE_RCC_CLOCK_MUX,
103 .version_id = 1,
104 .minimum_version_id = 1,
105 .fields = (VMStateField[]) {
106 VMSTATE_UINT32(id, RccClockMuxState),
107 VMSTATE_ARRAY_CLOCK(srcs, RccClockMuxState,
108 RCC_NUM_CLOCK_MUX_SRC),
109 VMSTATE_BOOL(enabled, RccClockMuxState),
110 VMSTATE_UINT32(src, RccClockMuxState),
111 VMSTATE_UINT32(multiplier, RccClockMuxState),
112 VMSTATE_UINT32(divider, RccClockMuxState),
113 VMSTATE_END_OF_LIST()
114 }
115};
116
117static void clock_mux_class_init(ObjectClass *klass, void *data)
118{
119 DeviceClass *dc = DEVICE_CLASS(klass);
120 ResettableClass *rc = RESETTABLE_CLASS(klass);
121
122 rc->phases.hold = clock_mux_reset_hold;
123 dc->vmsd = &clock_mux_vmstate;
124}
125
126static void clock_mux_set_enable(RccClockMuxState *mux, bool enabled)
127{
128 if (mux->enabled == enabled) {
129 return;
130 }
131
132 if (enabled) {
133 trace_stm32l4x5_rcc_mux_enable(mux->id);
134 } else {
135 trace_stm32l4x5_rcc_mux_disable(mux->id);
136 }
137
138 mux->enabled = enabled;
139 clock_mux_update(mux);
140}
141
142static void clock_mux_set_factor(RccClockMuxState *mux,
143 uint32_t multiplier, uint32_t divider)
144{
145 if (mux->multiplier == multiplier && mux->divider == divider) {
146 return;
147 }
148 trace_stm32l4x5_rcc_mux_set_factor(mux->id,
149 mux->multiplier, multiplier, mux->divider, divider);
150
151 mux->multiplier = multiplier;
152 mux->divider = divider;
153 clock_mux_update(mux);
154}
155
156static void clock_mux_set_source(RccClockMuxState *mux, RccClockMuxSource src)
157{
158 if (mux->src == src) {
159 return;
160 }
161
162 trace_stm32l4x5_rcc_mux_set_src(mux->id, mux->src, src);
163 mux->src = src;
164 clock_mux_update(mux);
165}
166
39static void rcc_update_irq(Stm32l4x5RccState *s)
40{
41 if (s->cifr & CIFR_IRQ_MASK) {
42 qemu_irq_raise(s->irq);
43 } else {
44 qemu_irq_lower(s->irq);
45 }
46}

--- 283 unchanged lines hidden (view full) ---

330 QDEV_CLOCK_IN(Stm32l4x5RccState, sai2_extclk, NULL, 0),
331 QDEV_CLOCK_END
332};
333
334
335static void stm32l4x5_rcc_init(Object *obj)
336{
337 Stm32l4x5RccState *s = STM32L4X5_RCC(obj);
167static void rcc_update_irq(Stm32l4x5RccState *s)
168{
169 if (s->cifr & CIFR_IRQ_MASK) {
170 qemu_irq_raise(s->irq);
171 } else {
172 qemu_irq_lower(s->irq);
173 }
174}

--- 283 unchanged lines hidden (view full) ---

458 QDEV_CLOCK_IN(Stm32l4x5RccState, sai2_extclk, NULL, 0),
459 QDEV_CLOCK_END
460};
461
462
463static void stm32l4x5_rcc_init(Object *obj)
464{
465 Stm32l4x5RccState *s = STM32L4X5_RCC(obj);
466 size_t i;
338
339 sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
340
341 memory_region_init_io(&s->mmio, obj, &stm32l4x5_rcc_ops, s,
342 TYPE_STM32L4X5_RCC, 0x400);
343 sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
344
345 qdev_init_clocks(DEVICE(s), stm32l4x5_rcc_clocks);
346
467
468 sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
469
470 memory_region_init_io(&s->mmio, obj, &stm32l4x5_rcc_ops, s,
471 TYPE_STM32L4X5_RCC, 0x400);
472 sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
473
474 qdev_init_clocks(DEVICE(s), stm32l4x5_rcc_clocks);
475
476 for (i = 0; i < RCC_NUM_CLOCK_MUX; i++) {
477
478 object_initialize_child(obj, "clock[*]",
479 &s->clock_muxes[i],
480 TYPE_RCC_CLOCK_MUX);
481
482 }
483
347 s->gnd = clock_new(obj, "gnd");
348}
349
350static const VMStateDescription vmstate_stm32l4x5_rcc = {
351 .name = TYPE_STM32L4X5_RCC,
352 .version_id = 1,
353 .minimum_version_id = 1,
354 .fields = (VMStateField[]) {

--- 36 unchanged lines hidden (view full) ---

391 VMSTATE_END_OF_LIST()
392 }
393};
394
395
396static void stm32l4x5_rcc_realize(DeviceState *dev, Error **errp)
397{
398 Stm32l4x5RccState *s = STM32L4X5_RCC(dev);
484 s->gnd = clock_new(obj, "gnd");
485}
486
487static const VMStateDescription vmstate_stm32l4x5_rcc = {
488 .name = TYPE_STM32L4X5_RCC,
489 .version_id = 1,
490 .minimum_version_id = 1,
491 .fields = (VMStateField[]) {

--- 36 unchanged lines hidden (view full) ---

528 VMSTATE_END_OF_LIST()
529 }
530};
531
532
533static void stm32l4x5_rcc_realize(DeviceState *dev, Error **errp)
534{
535 Stm32l4x5RccState *s = STM32L4X5_RCC(dev);
536 size_t i;
399
400 if (s->hse_frequency < 4000000ULL ||
401 s->hse_frequency > 48000000ULL) {
402 error_setg(errp,
403 "HSE frequency is outside of the allowed [4-48]Mhz range: %" PRIx64 "",
404 s->hse_frequency);
405 return;
406 }
407
537
538 if (s->hse_frequency < 4000000ULL ||
539 s->hse_frequency > 48000000ULL) {
540 error_setg(errp,
541 "HSE frequency is outside of the allowed [4-48]Mhz range: %" PRIx64 "",
542 s->hse_frequency);
543 return;
544 }
545
546 for (i = 0; i < RCC_NUM_CLOCK_MUX; i++) {
547 RccClockMuxState *clock_mux = &s->clock_muxes[i];
548
549 if (!qdev_realize(DEVICE(clock_mux), NULL, errp)) {
550 return;
551 }
552 }
553
408 clock_update_hz(s->msi_rc, MSI_DEFAULT_FRQ);
409 clock_update_hz(s->sai1_extclk, s->sai1_extclk_frequency);
410 clock_update_hz(s->sai2_extclk, s->sai2_extclk_frequency);
411 clock_update(s->gnd, 0);
554 clock_update_hz(s->msi_rc, MSI_DEFAULT_FRQ);
555 clock_update_hz(s->sai1_extclk, s->sai1_extclk_frequency);
556 clock_update_hz(s->sai2_extclk, s->sai2_extclk_frequency);
557 clock_update(s->gnd, 0);
558
559 /*
560 * Dummy values to make compilation pass.
561 * Removed in later commits.
562 */
563 clock_mux_set_source(&s->clock_muxes[0], RCC_CLOCK_MUX_SRC_GND);
564 clock_mux_set_enable(&s->clock_muxes[0], true);
565 clock_mux_set_factor(&s->clock_muxes[0], 1, 1);
412}
413
414static Property stm32l4x5_rcc_properties[] = {
415 DEFINE_PROP_UINT64("hse_frequency", Stm32l4x5RccState,
416 hse_frequency, HSE_DEFAULT_FRQ),
417 DEFINE_PROP_UINT64("sai1_extclk_frequency", Stm32l4x5RccState,
418 sai1_extclk_frequency, 0),
419 DEFINE_PROP_UINT64("sai2_extclk_frequency", Stm32l4x5RccState,

--- 15 unchanged lines hidden (view full) ---

435
436static const TypeInfo stm32l4x5_rcc_types[] = {
437 {
438 .name = TYPE_STM32L4X5_RCC,
439 .parent = TYPE_SYS_BUS_DEVICE,
440 .instance_size = sizeof(Stm32l4x5RccState),
441 .instance_init = stm32l4x5_rcc_init,
442 .class_init = stm32l4x5_rcc_class_init,
566}
567
568static Property stm32l4x5_rcc_properties[] = {
569 DEFINE_PROP_UINT64("hse_frequency", Stm32l4x5RccState,
570 hse_frequency, HSE_DEFAULT_FRQ),
571 DEFINE_PROP_UINT64("sai1_extclk_frequency", Stm32l4x5RccState,
572 sai1_extclk_frequency, 0),
573 DEFINE_PROP_UINT64("sai2_extclk_frequency", Stm32l4x5RccState,

--- 15 unchanged lines hidden (view full) ---

589
590static const TypeInfo stm32l4x5_rcc_types[] = {
591 {
592 .name = TYPE_STM32L4X5_RCC,
593 .parent = TYPE_SYS_BUS_DEVICE,
594 .instance_size = sizeof(Stm32l4x5RccState),
595 .instance_init = stm32l4x5_rcc_init,
596 .class_init = stm32l4x5_rcc_class_init,
597 }, {
598 .name = TYPE_RCC_CLOCK_MUX,
599 .parent = TYPE_DEVICE,
600 .instance_size = sizeof(RccClockMuxState),
601 .instance_init = clock_mux_init,
602 .class_init = clock_mux_class_init,
443 }
444};
445
446DEFINE_TYPES(stm32l4x5_rcc_types)
603 }
604};
605
606DEFINE_TYPES(stm32l4x5_rcc_types)