xref: /openbmc/qemu/hw/misc/stm32l4x5_rcc.c (revision ec7d83acbd1182d47df742745b43e6b16a3a4977)
1d6b55a0fSArnaud Minier /*
2d6b55a0fSArnaud Minier  * STM32L4X5 RCC (Reset and clock control)
3d6b55a0fSArnaud Minier  *
4d6b55a0fSArnaud Minier  * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
5d6b55a0fSArnaud Minier  * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr>
6d6b55a0fSArnaud Minier  *
7d6b55a0fSArnaud Minier  * SPDX-License-Identifier: GPL-2.0-or-later
8d6b55a0fSArnaud Minier  *
9d6b55a0fSArnaud Minier  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10d6b55a0fSArnaud Minier  * See the COPYING file in the top-level directory.
11d6b55a0fSArnaud Minier  *
12d6b55a0fSArnaud Minier  * The reference used is the STMicroElectronics RM0351 Reference manual
13d6b55a0fSArnaud Minier  * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs.
14d6b55a0fSArnaud Minier  *
15d6b55a0fSArnaud Minier  * Inspired by the BCM2835 CPRMAN clock manager implementation by Luc Michel.
16d6b55a0fSArnaud Minier  */
17d6b55a0fSArnaud Minier 
18d6b55a0fSArnaud Minier #include "qemu/osdep.h"
19d6b55a0fSArnaud Minier #include "qemu/log.h"
20d6b55a0fSArnaud Minier #include "qemu/module.h"
21d6b55a0fSArnaud Minier #include "qemu/timer.h"
22d6b55a0fSArnaud Minier #include "qapi/error.h"
23d6b55a0fSArnaud Minier #include "migration/vmstate.h"
24d6b55a0fSArnaud Minier #include "hw/misc/stm32l4x5_rcc.h"
25d6b55a0fSArnaud Minier #include "hw/misc/stm32l4x5_rcc_internals.h"
26d6b55a0fSArnaud Minier #include "hw/clock.h"
27d6b55a0fSArnaud Minier #include "hw/irq.h"
28d6b55a0fSArnaud Minier #include "hw/qdev-clock.h"
29d6b55a0fSArnaud Minier #include "hw/qdev-properties.h"
30d6b55a0fSArnaud Minier #include "hw/qdev-properties-system.h"
31d6b55a0fSArnaud Minier #include "trace.h"
32d6b55a0fSArnaud Minier 
33d6b55a0fSArnaud Minier #define HSE_DEFAULT_FRQ 48000000ULL
34d6b55a0fSArnaud Minier #define HSI_FRQ 16000000ULL
35d6b55a0fSArnaud Minier #define MSI_DEFAULT_FRQ 4000000ULL
36d6b55a0fSArnaud Minier #define LSE_FRQ 32768ULL
37d6b55a0fSArnaud Minier #define LSI_FRQ 32000ULL
38d6b55a0fSArnaud Minier 
39*ec7d83acSArnaud Minier static void clock_mux_update(RccClockMuxState *mux)
40*ec7d83acSArnaud Minier {
41*ec7d83acSArnaud Minier     uint64_t src_freq;
42*ec7d83acSArnaud Minier     Clock *current_source = mux->srcs[mux->src];
43*ec7d83acSArnaud Minier     uint32_t freq_multiplier = 0;
44*ec7d83acSArnaud Minier     /*
45*ec7d83acSArnaud Minier      * To avoid rounding errors, we use the clock period instead of the
46*ec7d83acSArnaud Minier      * frequency.
47*ec7d83acSArnaud Minier      * This means that the multiplier of the mux becomes the divider of
48*ec7d83acSArnaud Minier      * the clock and the divider of the mux becomes the multiplier of the
49*ec7d83acSArnaud Minier      * clock.
50*ec7d83acSArnaud Minier      */
51*ec7d83acSArnaud Minier     if (mux->enabled && mux->divider) {
52*ec7d83acSArnaud Minier         freq_multiplier = mux->divider;
53*ec7d83acSArnaud Minier     }
54*ec7d83acSArnaud Minier 
55*ec7d83acSArnaud Minier     clock_set_mul_div(mux->out, freq_multiplier, mux->multiplier);
56*ec7d83acSArnaud Minier     clock_update(mux->out, clock_get(current_source));
57*ec7d83acSArnaud Minier 
58*ec7d83acSArnaud Minier     src_freq = clock_get_hz(current_source);
59*ec7d83acSArnaud Minier     /* TODO: can we simply detect if the config changed so that we reduce log spam ? */
60*ec7d83acSArnaud Minier     trace_stm32l4x5_rcc_mux_update(mux->id, mux->src, src_freq,
61*ec7d83acSArnaud Minier                                    mux->multiplier, mux->divider);
62*ec7d83acSArnaud Minier }
63*ec7d83acSArnaud Minier 
64*ec7d83acSArnaud Minier static void clock_mux_src_update(void *opaque, ClockEvent event)
65*ec7d83acSArnaud Minier {
66*ec7d83acSArnaud Minier     RccClockMuxState **backref = opaque;
67*ec7d83acSArnaud Minier     RccClockMuxState *s = *backref;
68*ec7d83acSArnaud Minier     /*
69*ec7d83acSArnaud Minier      * The backref value is equal to:
70*ec7d83acSArnaud Minier      * s->backref + (sizeof(RccClockMuxState *) * update_src).
71*ec7d83acSArnaud Minier      * By subtracting we can get back the index of the updated clock.
72*ec7d83acSArnaud Minier      */
73*ec7d83acSArnaud Minier     const uint32_t update_src = backref - s->backref;
74*ec7d83acSArnaud Minier     /* Only update if the clock that was updated is the current source */
75*ec7d83acSArnaud Minier     if (update_src == s->src) {
76*ec7d83acSArnaud Minier         clock_mux_update(s);
77*ec7d83acSArnaud Minier     }
78*ec7d83acSArnaud Minier }
79*ec7d83acSArnaud Minier 
80*ec7d83acSArnaud Minier static void clock_mux_init(Object *obj)
81*ec7d83acSArnaud Minier {
82*ec7d83acSArnaud Minier     RccClockMuxState *s = RCC_CLOCK_MUX(obj);
83*ec7d83acSArnaud Minier     size_t i;
84*ec7d83acSArnaud Minier 
85*ec7d83acSArnaud Minier     for (i = 0; i < RCC_NUM_CLOCK_MUX_SRC; i++) {
86*ec7d83acSArnaud Minier         char *name = g_strdup_printf("srcs[%zu]", i);
87*ec7d83acSArnaud Minier         s->backref[i] = s;
88*ec7d83acSArnaud Minier         s->srcs[i] = qdev_init_clock_in(DEVICE(s), name,
89*ec7d83acSArnaud Minier                                         clock_mux_src_update,
90*ec7d83acSArnaud Minier                                         &s->backref[i],
91*ec7d83acSArnaud Minier                                         ClockUpdate);
92*ec7d83acSArnaud Minier         g_free(name);
93*ec7d83acSArnaud Minier     }
94*ec7d83acSArnaud Minier 
95*ec7d83acSArnaud Minier     s->out = qdev_init_clock_out(DEVICE(s), "out");
96*ec7d83acSArnaud Minier }
97*ec7d83acSArnaud Minier 
98*ec7d83acSArnaud Minier static void clock_mux_reset_hold(Object *obj)
99*ec7d83acSArnaud Minier { }
100*ec7d83acSArnaud Minier 
101*ec7d83acSArnaud Minier static const VMStateDescription clock_mux_vmstate = {
102*ec7d83acSArnaud Minier     .name = TYPE_RCC_CLOCK_MUX,
103*ec7d83acSArnaud Minier     .version_id = 1,
104*ec7d83acSArnaud Minier     .minimum_version_id = 1,
105*ec7d83acSArnaud Minier     .fields = (VMStateField[]) {
106*ec7d83acSArnaud Minier         VMSTATE_UINT32(id, RccClockMuxState),
107*ec7d83acSArnaud Minier         VMSTATE_ARRAY_CLOCK(srcs, RccClockMuxState,
108*ec7d83acSArnaud Minier                             RCC_NUM_CLOCK_MUX_SRC),
109*ec7d83acSArnaud Minier         VMSTATE_BOOL(enabled, RccClockMuxState),
110*ec7d83acSArnaud Minier         VMSTATE_UINT32(src, RccClockMuxState),
111*ec7d83acSArnaud Minier         VMSTATE_UINT32(multiplier, RccClockMuxState),
112*ec7d83acSArnaud Minier         VMSTATE_UINT32(divider, RccClockMuxState),
113*ec7d83acSArnaud Minier         VMSTATE_END_OF_LIST()
114*ec7d83acSArnaud Minier     }
115*ec7d83acSArnaud Minier };
116*ec7d83acSArnaud Minier 
117*ec7d83acSArnaud Minier static void clock_mux_class_init(ObjectClass *klass, void *data)
118*ec7d83acSArnaud Minier {
119*ec7d83acSArnaud Minier     DeviceClass *dc = DEVICE_CLASS(klass);
120*ec7d83acSArnaud Minier     ResettableClass *rc = RESETTABLE_CLASS(klass);
121*ec7d83acSArnaud Minier 
122*ec7d83acSArnaud Minier     rc->phases.hold = clock_mux_reset_hold;
123*ec7d83acSArnaud Minier     dc->vmsd = &clock_mux_vmstate;
124*ec7d83acSArnaud Minier }
125*ec7d83acSArnaud Minier 
126*ec7d83acSArnaud Minier static void clock_mux_set_enable(RccClockMuxState *mux, bool enabled)
127*ec7d83acSArnaud Minier {
128*ec7d83acSArnaud Minier     if (mux->enabled == enabled) {
129*ec7d83acSArnaud Minier         return;
130*ec7d83acSArnaud Minier     }
131*ec7d83acSArnaud Minier 
132*ec7d83acSArnaud Minier     if (enabled) {
133*ec7d83acSArnaud Minier         trace_stm32l4x5_rcc_mux_enable(mux->id);
134*ec7d83acSArnaud Minier     } else {
135*ec7d83acSArnaud Minier         trace_stm32l4x5_rcc_mux_disable(mux->id);
136*ec7d83acSArnaud Minier     }
137*ec7d83acSArnaud Minier 
138*ec7d83acSArnaud Minier     mux->enabled = enabled;
139*ec7d83acSArnaud Minier     clock_mux_update(mux);
140*ec7d83acSArnaud Minier }
141*ec7d83acSArnaud Minier 
142*ec7d83acSArnaud Minier static void clock_mux_set_factor(RccClockMuxState *mux,
143*ec7d83acSArnaud Minier                                  uint32_t multiplier, uint32_t divider)
144*ec7d83acSArnaud Minier {
145*ec7d83acSArnaud Minier     if (mux->multiplier == multiplier && mux->divider == divider) {
146*ec7d83acSArnaud Minier         return;
147*ec7d83acSArnaud Minier     }
148*ec7d83acSArnaud Minier     trace_stm32l4x5_rcc_mux_set_factor(mux->id,
149*ec7d83acSArnaud Minier         mux->multiplier, multiplier, mux->divider, divider);
150*ec7d83acSArnaud Minier 
151*ec7d83acSArnaud Minier     mux->multiplier = multiplier;
152*ec7d83acSArnaud Minier     mux->divider = divider;
153*ec7d83acSArnaud Minier     clock_mux_update(mux);
154*ec7d83acSArnaud Minier }
155*ec7d83acSArnaud Minier 
156*ec7d83acSArnaud Minier static void clock_mux_set_source(RccClockMuxState *mux, RccClockMuxSource src)
157*ec7d83acSArnaud Minier {
158*ec7d83acSArnaud Minier     if (mux->src == src) {
159*ec7d83acSArnaud Minier         return;
160*ec7d83acSArnaud Minier     }
161*ec7d83acSArnaud Minier 
162*ec7d83acSArnaud Minier     trace_stm32l4x5_rcc_mux_set_src(mux->id, mux->src, src);
163*ec7d83acSArnaud Minier     mux->src = src;
164*ec7d83acSArnaud Minier     clock_mux_update(mux);
165*ec7d83acSArnaud Minier }
166*ec7d83acSArnaud Minier 
167d6b55a0fSArnaud Minier static void rcc_update_irq(Stm32l4x5RccState *s)
168d6b55a0fSArnaud Minier {
169d6b55a0fSArnaud Minier     if (s->cifr & CIFR_IRQ_MASK) {
170d6b55a0fSArnaud Minier         qemu_irq_raise(s->irq);
171d6b55a0fSArnaud Minier     } else {
172d6b55a0fSArnaud Minier         qemu_irq_lower(s->irq);
173d6b55a0fSArnaud Minier     }
174d6b55a0fSArnaud Minier }
175d6b55a0fSArnaud Minier 
176d6b55a0fSArnaud Minier static void stm32l4x5_rcc_reset_hold(Object *obj)
177d6b55a0fSArnaud Minier {
178d6b55a0fSArnaud Minier     Stm32l4x5RccState *s = STM32L4X5_RCC(obj);
179d6b55a0fSArnaud Minier     s->cr = 0x00000063;
180d6b55a0fSArnaud Minier     /*
181d6b55a0fSArnaud Minier      * Factory-programmed calibration data
182d6b55a0fSArnaud Minier      * From the reference manual: 0x10XX 00XX
183d6b55a0fSArnaud Minier      * Value taken from a real card.
184d6b55a0fSArnaud Minier      */
185d6b55a0fSArnaud Minier     s->icscr = 0x106E0082;
186d6b55a0fSArnaud Minier     s->cfgr = 0x0;
187d6b55a0fSArnaud Minier     s->pllcfgr = 0x00001000;
188d6b55a0fSArnaud Minier     s->pllsai1cfgr = 0x00001000;
189d6b55a0fSArnaud Minier     s->pllsai2cfgr = 0x00001000;
190d6b55a0fSArnaud Minier     s->cier = 0x0;
191d6b55a0fSArnaud Minier     s->cifr = 0x0;
192d6b55a0fSArnaud Minier     s->ahb1rstr = 0x0;
193d6b55a0fSArnaud Minier     s->ahb2rstr = 0x0;
194d6b55a0fSArnaud Minier     s->ahb3rstr = 0x0;
195d6b55a0fSArnaud Minier     s->apb1rstr1 = 0x0;
196d6b55a0fSArnaud Minier     s->apb1rstr2 = 0x0;
197d6b55a0fSArnaud Minier     s->apb2rstr = 0x0;
198d6b55a0fSArnaud Minier     s->ahb1enr = 0x00000100;
199d6b55a0fSArnaud Minier     s->ahb2enr = 0x0;
200d6b55a0fSArnaud Minier     s->ahb3enr = 0x0;
201d6b55a0fSArnaud Minier     s->apb1enr1 = 0x0;
202d6b55a0fSArnaud Minier     s->apb1enr2 = 0x0;
203d6b55a0fSArnaud Minier     s->apb2enr = 0x0;
204d6b55a0fSArnaud Minier     s->ahb1smenr = 0x00011303;
205d6b55a0fSArnaud Minier     s->ahb2smenr = 0x000532FF;
206d6b55a0fSArnaud Minier     s->ahb3smenr =  0x00000101;
207d6b55a0fSArnaud Minier     s->apb1smenr1 = 0xF2FECA3F;
208d6b55a0fSArnaud Minier     s->apb1smenr2 = 0x00000025;
209d6b55a0fSArnaud Minier     s->apb2smenr = 0x01677C01;
210d6b55a0fSArnaud Minier     s->ccipr = 0x0;
211d6b55a0fSArnaud Minier     s->bdcr = 0x0;
212d6b55a0fSArnaud Minier     s->csr = 0x0C000600;
213d6b55a0fSArnaud Minier }
214d6b55a0fSArnaud Minier 
215d6b55a0fSArnaud Minier static uint64_t stm32l4x5_rcc_read(void *opaque, hwaddr addr,
216d6b55a0fSArnaud Minier                                      unsigned int size)
217d6b55a0fSArnaud Minier {
218d6b55a0fSArnaud Minier     Stm32l4x5RccState *s = opaque;
219d6b55a0fSArnaud Minier     uint64_t retvalue = 0;
220d6b55a0fSArnaud Minier 
221d6b55a0fSArnaud Minier     switch (addr) {
222d6b55a0fSArnaud Minier     case A_CR:
223d6b55a0fSArnaud Minier         retvalue = s->cr;
224d6b55a0fSArnaud Minier         break;
225d6b55a0fSArnaud Minier     case A_ICSCR:
226d6b55a0fSArnaud Minier         retvalue = s->icscr;
227d6b55a0fSArnaud Minier         break;
228d6b55a0fSArnaud Minier     case A_CFGR:
229d6b55a0fSArnaud Minier         retvalue = s->cfgr;
230d6b55a0fSArnaud Minier         break;
231d6b55a0fSArnaud Minier     case A_PLLCFGR:
232d6b55a0fSArnaud Minier         retvalue = s->pllcfgr;
233d6b55a0fSArnaud Minier         break;
234d6b55a0fSArnaud Minier     case A_PLLSAI1CFGR:
235d6b55a0fSArnaud Minier         retvalue = s->pllsai1cfgr;
236d6b55a0fSArnaud Minier         break;
237d6b55a0fSArnaud Minier     case A_PLLSAI2CFGR:
238d6b55a0fSArnaud Minier         retvalue = s->pllsai2cfgr;
239d6b55a0fSArnaud Minier         break;
240d6b55a0fSArnaud Minier     case A_CIER:
241d6b55a0fSArnaud Minier         retvalue = s->cier;
242d6b55a0fSArnaud Minier         break;
243d6b55a0fSArnaud Minier     case A_CIFR:
244d6b55a0fSArnaud Minier         retvalue = s->cifr;
245d6b55a0fSArnaud Minier         break;
246d6b55a0fSArnaud Minier     case A_CICR:
247d6b55a0fSArnaud Minier         /* CICR is write only, return the reset value = 0 */
248d6b55a0fSArnaud Minier         break;
249d6b55a0fSArnaud Minier     case A_AHB1RSTR:
250d6b55a0fSArnaud Minier         retvalue = s->ahb1rstr;
251d6b55a0fSArnaud Minier         break;
252d6b55a0fSArnaud Minier     case A_AHB2RSTR:
253d6b55a0fSArnaud Minier         retvalue = s->ahb2rstr;
254d6b55a0fSArnaud Minier         break;
255d6b55a0fSArnaud Minier     case A_AHB3RSTR:
256d6b55a0fSArnaud Minier         retvalue = s->ahb3rstr;
257d6b55a0fSArnaud Minier         break;
258d6b55a0fSArnaud Minier     case A_APB1RSTR1:
259d6b55a0fSArnaud Minier         retvalue = s->apb1rstr1;
260d6b55a0fSArnaud Minier         break;
261d6b55a0fSArnaud Minier     case A_APB1RSTR2:
262d6b55a0fSArnaud Minier         retvalue = s->apb1rstr2;
263d6b55a0fSArnaud Minier         break;
264d6b55a0fSArnaud Minier     case A_APB2RSTR:
265d6b55a0fSArnaud Minier         retvalue = s->apb2rstr;
266d6b55a0fSArnaud Minier         break;
267d6b55a0fSArnaud Minier     case A_AHB1ENR:
268d6b55a0fSArnaud Minier         retvalue = s->ahb1enr;
269d6b55a0fSArnaud Minier         break;
270d6b55a0fSArnaud Minier     case A_AHB2ENR:
271d6b55a0fSArnaud Minier         retvalue = s->ahb2enr;
272d6b55a0fSArnaud Minier         break;
273d6b55a0fSArnaud Minier     case A_AHB3ENR:
274d6b55a0fSArnaud Minier         retvalue = s->ahb3enr;
275d6b55a0fSArnaud Minier         break;
276d6b55a0fSArnaud Minier     case A_APB1ENR1:
277d6b55a0fSArnaud Minier         retvalue = s->apb1enr1;
278d6b55a0fSArnaud Minier         break;
279d6b55a0fSArnaud Minier     case A_APB1ENR2:
280d6b55a0fSArnaud Minier         retvalue = s->apb1enr2;
281d6b55a0fSArnaud Minier         break;
282d6b55a0fSArnaud Minier     case A_APB2ENR:
283d6b55a0fSArnaud Minier         retvalue = s->apb2enr;
284d6b55a0fSArnaud Minier         break;
285d6b55a0fSArnaud Minier     case A_AHB1SMENR:
286d6b55a0fSArnaud Minier         retvalue = s->ahb1smenr;
287d6b55a0fSArnaud Minier         break;
288d6b55a0fSArnaud Minier     case A_AHB2SMENR:
289d6b55a0fSArnaud Minier         retvalue = s->ahb2smenr;
290d6b55a0fSArnaud Minier         break;
291d6b55a0fSArnaud Minier     case A_AHB3SMENR:
292d6b55a0fSArnaud Minier         retvalue = s->ahb3smenr;
293d6b55a0fSArnaud Minier         break;
294d6b55a0fSArnaud Minier     case A_APB1SMENR1:
295d6b55a0fSArnaud Minier         retvalue = s->apb1smenr1;
296d6b55a0fSArnaud Minier         break;
297d6b55a0fSArnaud Minier     case A_APB1SMENR2:
298d6b55a0fSArnaud Minier         retvalue = s->apb1smenr2;
299d6b55a0fSArnaud Minier         break;
300d6b55a0fSArnaud Minier     case A_APB2SMENR:
301d6b55a0fSArnaud Minier         retvalue = s->apb2smenr;
302d6b55a0fSArnaud Minier         break;
303d6b55a0fSArnaud Minier     case A_CCIPR:
304d6b55a0fSArnaud Minier         retvalue = s->ccipr;
305d6b55a0fSArnaud Minier         break;
306d6b55a0fSArnaud Minier     case A_BDCR:
307d6b55a0fSArnaud Minier         retvalue = s->bdcr;
308d6b55a0fSArnaud Minier         break;
309d6b55a0fSArnaud Minier     case A_CSR:
310d6b55a0fSArnaud Minier         retvalue = s->csr;
311d6b55a0fSArnaud Minier         break;
312d6b55a0fSArnaud Minier     default:
313d6b55a0fSArnaud Minier         qemu_log_mask(LOG_GUEST_ERROR,
314d6b55a0fSArnaud Minier                       "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
315d6b55a0fSArnaud Minier         break;
316d6b55a0fSArnaud Minier     }
317d6b55a0fSArnaud Minier 
318d6b55a0fSArnaud Minier     trace_stm32l4x5_rcc_read(addr, retvalue);
319d6b55a0fSArnaud Minier 
320d6b55a0fSArnaud Minier     return retvalue;
321d6b55a0fSArnaud Minier }
322d6b55a0fSArnaud Minier 
323d6b55a0fSArnaud Minier static void stm32l4x5_rcc_write(void *opaque, hwaddr addr,
324d6b55a0fSArnaud Minier                                   uint64_t val64, unsigned int size)
325d6b55a0fSArnaud Minier {
326d6b55a0fSArnaud Minier     Stm32l4x5RccState *s = opaque;
327d6b55a0fSArnaud Minier     const uint32_t value = val64;
328d6b55a0fSArnaud Minier 
329d6b55a0fSArnaud Minier     trace_stm32l4x5_rcc_write(addr, value);
330d6b55a0fSArnaud Minier 
331d6b55a0fSArnaud Minier     switch (addr) {
332d6b55a0fSArnaud Minier     case A_CR:
333d6b55a0fSArnaud Minier         s->cr = (s->cr & CR_READ_SET_MASK) |
334d6b55a0fSArnaud Minier                 (value & (CR_READ_SET_MASK | ~CR_READ_ONLY_MASK));
335d6b55a0fSArnaud Minier         break;
336d6b55a0fSArnaud Minier     case A_ICSCR:
337d6b55a0fSArnaud Minier         s->icscr = value & ~ICSCR_READ_ONLY_MASK;
338d6b55a0fSArnaud Minier         break;
339d6b55a0fSArnaud Minier     case A_CFGR:
340d6b55a0fSArnaud Minier         s->cfgr = value & ~CFGR_READ_ONLY_MASK;
341d6b55a0fSArnaud Minier         break;
342d6b55a0fSArnaud Minier     case A_PLLCFGR:
343d6b55a0fSArnaud Minier         s->pllcfgr = value;
344d6b55a0fSArnaud Minier         break;
345d6b55a0fSArnaud Minier     case A_PLLSAI1CFGR:
346d6b55a0fSArnaud Minier         s->pllsai1cfgr = value;
347d6b55a0fSArnaud Minier         break;
348d6b55a0fSArnaud Minier     case A_PLLSAI2CFGR:
349d6b55a0fSArnaud Minier         s->pllsai2cfgr = value;
350d6b55a0fSArnaud Minier         break;
351d6b55a0fSArnaud Minier     case A_CIER:
352d6b55a0fSArnaud Minier         s->cier = value;
353d6b55a0fSArnaud Minier         break;
354d6b55a0fSArnaud Minier     case A_CIFR:
355d6b55a0fSArnaud Minier         qemu_log_mask(LOG_GUEST_ERROR,
356d6b55a0fSArnaud Minier             "%s: Write attempt into read-only register (CIFR) 0x%"PRIx32"\n",
357d6b55a0fSArnaud Minier             __func__, value);
358d6b55a0fSArnaud Minier         break;
359d6b55a0fSArnaud Minier     case A_CICR:
360d6b55a0fSArnaud Minier         /* Clear interrupt flags by writing a 1 to the CICR register */
361d6b55a0fSArnaud Minier         s->cifr &= ~value;
362d6b55a0fSArnaud Minier         rcc_update_irq(s);
363d6b55a0fSArnaud Minier         break;
364d6b55a0fSArnaud Minier     /* Reset behaviors are not implemented */
365d6b55a0fSArnaud Minier     case A_AHB1RSTR:
366d6b55a0fSArnaud Minier         s->ahb1rstr = value;
367d6b55a0fSArnaud Minier         break;
368d6b55a0fSArnaud Minier     case A_AHB2RSTR:
369d6b55a0fSArnaud Minier         s->ahb2rstr = value;
370d6b55a0fSArnaud Minier         break;
371d6b55a0fSArnaud Minier     case A_AHB3RSTR:
372d6b55a0fSArnaud Minier         s->ahb3rstr = value;
373d6b55a0fSArnaud Minier         break;
374d6b55a0fSArnaud Minier     case A_APB1RSTR1:
375d6b55a0fSArnaud Minier         s->apb1rstr1 = value;
376d6b55a0fSArnaud Minier         break;
377d6b55a0fSArnaud Minier     case A_APB1RSTR2:
378d6b55a0fSArnaud Minier         s->apb1rstr2 = value;
379d6b55a0fSArnaud Minier         break;
380d6b55a0fSArnaud Minier     case A_APB2RSTR:
381d6b55a0fSArnaud Minier         s->apb2rstr = value;
382d6b55a0fSArnaud Minier         break;
383d6b55a0fSArnaud Minier     case A_AHB1ENR:
384d6b55a0fSArnaud Minier         s->ahb1enr = value;
385d6b55a0fSArnaud Minier         break;
386d6b55a0fSArnaud Minier     case A_AHB2ENR:
387d6b55a0fSArnaud Minier         s->ahb2enr = value;
388d6b55a0fSArnaud Minier         break;
389d6b55a0fSArnaud Minier     case A_AHB3ENR:
390d6b55a0fSArnaud Minier         s->ahb3enr = value;
391d6b55a0fSArnaud Minier         break;
392d6b55a0fSArnaud Minier     case A_APB1ENR1:
393d6b55a0fSArnaud Minier         s->apb1enr1 = value;
394d6b55a0fSArnaud Minier         break;
395d6b55a0fSArnaud Minier     case A_APB1ENR2:
396d6b55a0fSArnaud Minier         s->apb1enr2 = value;
397d6b55a0fSArnaud Minier         break;
398d6b55a0fSArnaud Minier     case A_APB2ENR:
399d6b55a0fSArnaud Minier         s->apb2enr = (s->apb2enr & APB2ENR_READ_SET_MASK) | value;
400d6b55a0fSArnaud Minier         break;
401d6b55a0fSArnaud Minier     /* Behaviors for Sleep and Stop modes are not implemented */
402d6b55a0fSArnaud Minier     case A_AHB1SMENR:
403d6b55a0fSArnaud Minier         s->ahb1smenr = value;
404d6b55a0fSArnaud Minier         break;
405d6b55a0fSArnaud Minier     case A_AHB2SMENR:
406d6b55a0fSArnaud Minier         s->ahb2smenr = value;
407d6b55a0fSArnaud Minier         break;
408d6b55a0fSArnaud Minier     case A_AHB3SMENR:
409d6b55a0fSArnaud Minier         s->ahb3smenr = value;
410d6b55a0fSArnaud Minier         break;
411d6b55a0fSArnaud Minier     case A_APB1SMENR1:
412d6b55a0fSArnaud Minier         s->apb1smenr1 = value;
413d6b55a0fSArnaud Minier         break;
414d6b55a0fSArnaud Minier     case A_APB1SMENR2:
415d6b55a0fSArnaud Minier         s->apb1smenr2 = value;
416d6b55a0fSArnaud Minier         break;
417d6b55a0fSArnaud Minier     case A_APB2SMENR:
418d6b55a0fSArnaud Minier         s->apb2smenr = value;
419d6b55a0fSArnaud Minier         break;
420d6b55a0fSArnaud Minier     case A_CCIPR:
421d6b55a0fSArnaud Minier         s->ccipr = value;
422d6b55a0fSArnaud Minier         break;
423d6b55a0fSArnaud Minier     case A_BDCR:
424d6b55a0fSArnaud Minier         s->bdcr = value & ~BDCR_READ_ONLY_MASK;
425d6b55a0fSArnaud Minier         break;
426d6b55a0fSArnaud Minier     case A_CSR:
427d6b55a0fSArnaud Minier         s->csr = value & ~CSR_READ_ONLY_MASK;
428d6b55a0fSArnaud Minier         break;
429d6b55a0fSArnaud Minier     default:
430d6b55a0fSArnaud Minier         qemu_log_mask(LOG_GUEST_ERROR,
431d6b55a0fSArnaud Minier                       "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
432d6b55a0fSArnaud Minier     }
433d6b55a0fSArnaud Minier }
434d6b55a0fSArnaud Minier 
435d6b55a0fSArnaud Minier static const MemoryRegionOps stm32l4x5_rcc_ops = {
436d6b55a0fSArnaud Minier     .read = stm32l4x5_rcc_read,
437d6b55a0fSArnaud Minier     .write = stm32l4x5_rcc_write,
438d6b55a0fSArnaud Minier     .endianness = DEVICE_NATIVE_ENDIAN,
439d6b55a0fSArnaud Minier     .valid = {
440d6b55a0fSArnaud Minier         .max_access_size = 4,
441d6b55a0fSArnaud Minier         .min_access_size = 4,
442d6b55a0fSArnaud Minier         .unaligned = false
443d6b55a0fSArnaud Minier     },
444d6b55a0fSArnaud Minier     .impl = {
445d6b55a0fSArnaud Minier         .max_access_size = 4,
446d6b55a0fSArnaud Minier         .min_access_size = 4,
447d6b55a0fSArnaud Minier         .unaligned = false
448d6b55a0fSArnaud Minier     },
449d6b55a0fSArnaud Minier };
450d6b55a0fSArnaud Minier 
451d6b55a0fSArnaud Minier static const ClockPortInitArray stm32l4x5_rcc_clocks = {
452d6b55a0fSArnaud Minier     QDEV_CLOCK_IN(Stm32l4x5RccState, hsi16_rc, NULL, 0),
453d6b55a0fSArnaud Minier     QDEV_CLOCK_IN(Stm32l4x5RccState, msi_rc, NULL, 0),
454d6b55a0fSArnaud Minier     QDEV_CLOCK_IN(Stm32l4x5RccState, hse, NULL, 0),
455d6b55a0fSArnaud Minier     QDEV_CLOCK_IN(Stm32l4x5RccState, lsi_rc, NULL, 0),
456d6b55a0fSArnaud Minier     QDEV_CLOCK_IN(Stm32l4x5RccState, lse_crystal, NULL, 0),
457d6b55a0fSArnaud Minier     QDEV_CLOCK_IN(Stm32l4x5RccState, sai1_extclk, NULL, 0),
458d6b55a0fSArnaud Minier     QDEV_CLOCK_IN(Stm32l4x5RccState, sai2_extclk, NULL, 0),
459d6b55a0fSArnaud Minier     QDEV_CLOCK_END
460d6b55a0fSArnaud Minier };
461d6b55a0fSArnaud Minier 
462d6b55a0fSArnaud Minier 
463d6b55a0fSArnaud Minier static void stm32l4x5_rcc_init(Object *obj)
464d6b55a0fSArnaud Minier {
465d6b55a0fSArnaud Minier     Stm32l4x5RccState *s = STM32L4X5_RCC(obj);
466*ec7d83acSArnaud Minier     size_t i;
467d6b55a0fSArnaud Minier 
468d6b55a0fSArnaud Minier     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
469d6b55a0fSArnaud Minier 
470d6b55a0fSArnaud Minier     memory_region_init_io(&s->mmio, obj, &stm32l4x5_rcc_ops, s,
471d6b55a0fSArnaud Minier                           TYPE_STM32L4X5_RCC, 0x400);
472d6b55a0fSArnaud Minier     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
473d6b55a0fSArnaud Minier 
474d6b55a0fSArnaud Minier     qdev_init_clocks(DEVICE(s), stm32l4x5_rcc_clocks);
475d6b55a0fSArnaud Minier 
476*ec7d83acSArnaud Minier     for (i = 0; i < RCC_NUM_CLOCK_MUX; i++) {
477*ec7d83acSArnaud Minier 
478*ec7d83acSArnaud Minier         object_initialize_child(obj, "clock[*]",
479*ec7d83acSArnaud Minier                                 &s->clock_muxes[i],
480*ec7d83acSArnaud Minier                                 TYPE_RCC_CLOCK_MUX);
481*ec7d83acSArnaud Minier 
482*ec7d83acSArnaud Minier     }
483*ec7d83acSArnaud Minier 
484d6b55a0fSArnaud Minier     s->gnd = clock_new(obj, "gnd");
485d6b55a0fSArnaud Minier }
486d6b55a0fSArnaud Minier 
487d6b55a0fSArnaud Minier static const VMStateDescription vmstate_stm32l4x5_rcc = {
488d6b55a0fSArnaud Minier     .name = TYPE_STM32L4X5_RCC,
489d6b55a0fSArnaud Minier     .version_id = 1,
490d6b55a0fSArnaud Minier     .minimum_version_id = 1,
491d6b55a0fSArnaud Minier     .fields = (VMStateField[]) {
492d6b55a0fSArnaud Minier         VMSTATE_UINT32(cr, Stm32l4x5RccState),
493d6b55a0fSArnaud Minier         VMSTATE_UINT32(icscr, Stm32l4x5RccState),
494d6b55a0fSArnaud Minier         VMSTATE_UINT32(cfgr, Stm32l4x5RccState),
495d6b55a0fSArnaud Minier         VMSTATE_UINT32(pllcfgr, Stm32l4x5RccState),
496d6b55a0fSArnaud Minier         VMSTATE_UINT32(pllsai1cfgr, Stm32l4x5RccState),
497d6b55a0fSArnaud Minier         VMSTATE_UINT32(pllsai2cfgr, Stm32l4x5RccState),
498d6b55a0fSArnaud Minier         VMSTATE_UINT32(cier, Stm32l4x5RccState),
499d6b55a0fSArnaud Minier         VMSTATE_UINT32(cifr, Stm32l4x5RccState),
500d6b55a0fSArnaud Minier         VMSTATE_UINT32(ahb1rstr, Stm32l4x5RccState),
501d6b55a0fSArnaud Minier         VMSTATE_UINT32(ahb2rstr, Stm32l4x5RccState),
502d6b55a0fSArnaud Minier         VMSTATE_UINT32(ahb3rstr, Stm32l4x5RccState),
503d6b55a0fSArnaud Minier         VMSTATE_UINT32(apb1rstr1, Stm32l4x5RccState),
504d6b55a0fSArnaud Minier         VMSTATE_UINT32(apb1rstr2, Stm32l4x5RccState),
505d6b55a0fSArnaud Minier         VMSTATE_UINT32(apb2rstr, Stm32l4x5RccState),
506d6b55a0fSArnaud Minier         VMSTATE_UINT32(ahb1enr, Stm32l4x5RccState),
507d6b55a0fSArnaud Minier         VMSTATE_UINT32(ahb2enr, Stm32l4x5RccState),
508d6b55a0fSArnaud Minier         VMSTATE_UINT32(ahb3enr, Stm32l4x5RccState),
509d6b55a0fSArnaud Minier         VMSTATE_UINT32(apb1enr1, Stm32l4x5RccState),
510d6b55a0fSArnaud Minier         VMSTATE_UINT32(apb1enr2, Stm32l4x5RccState),
511d6b55a0fSArnaud Minier         VMSTATE_UINT32(apb2enr, Stm32l4x5RccState),
512d6b55a0fSArnaud Minier         VMSTATE_UINT32(ahb1smenr, Stm32l4x5RccState),
513d6b55a0fSArnaud Minier         VMSTATE_UINT32(ahb2smenr, Stm32l4x5RccState),
514d6b55a0fSArnaud Minier         VMSTATE_UINT32(ahb3smenr, Stm32l4x5RccState),
515d6b55a0fSArnaud Minier         VMSTATE_UINT32(apb1smenr1, Stm32l4x5RccState),
516d6b55a0fSArnaud Minier         VMSTATE_UINT32(apb1smenr2, Stm32l4x5RccState),
517d6b55a0fSArnaud Minier         VMSTATE_UINT32(apb2smenr, Stm32l4x5RccState),
518d6b55a0fSArnaud Minier         VMSTATE_UINT32(ccipr, Stm32l4x5RccState),
519d6b55a0fSArnaud Minier         VMSTATE_UINT32(bdcr, Stm32l4x5RccState),
520d6b55a0fSArnaud Minier         VMSTATE_UINT32(csr, Stm32l4x5RccState),
521d6b55a0fSArnaud Minier         VMSTATE_CLOCK(hsi16_rc, Stm32l4x5RccState),
522d6b55a0fSArnaud Minier         VMSTATE_CLOCK(msi_rc, Stm32l4x5RccState),
523d6b55a0fSArnaud Minier         VMSTATE_CLOCK(hse, Stm32l4x5RccState),
524d6b55a0fSArnaud Minier         VMSTATE_CLOCK(lsi_rc, Stm32l4x5RccState),
525d6b55a0fSArnaud Minier         VMSTATE_CLOCK(lse_crystal, Stm32l4x5RccState),
526d6b55a0fSArnaud Minier         VMSTATE_CLOCK(sai1_extclk, Stm32l4x5RccState),
527d6b55a0fSArnaud Minier         VMSTATE_CLOCK(sai2_extclk, Stm32l4x5RccState),
528d6b55a0fSArnaud Minier         VMSTATE_END_OF_LIST()
529d6b55a0fSArnaud Minier     }
530d6b55a0fSArnaud Minier };
531d6b55a0fSArnaud Minier 
532d6b55a0fSArnaud Minier 
533d6b55a0fSArnaud Minier static void stm32l4x5_rcc_realize(DeviceState *dev, Error **errp)
534d6b55a0fSArnaud Minier {
535d6b55a0fSArnaud Minier     Stm32l4x5RccState *s = STM32L4X5_RCC(dev);
536*ec7d83acSArnaud Minier     size_t i;
537d6b55a0fSArnaud Minier 
538d6b55a0fSArnaud Minier     if (s->hse_frequency <  4000000ULL ||
539d6b55a0fSArnaud Minier         s->hse_frequency > 48000000ULL) {
540d6b55a0fSArnaud Minier             error_setg(errp,
541d6b55a0fSArnaud Minier                 "HSE frequency is outside of the allowed [4-48]Mhz range: %" PRIx64 "",
542d6b55a0fSArnaud Minier                 s->hse_frequency);
543d6b55a0fSArnaud Minier             return;
544d6b55a0fSArnaud Minier         }
545d6b55a0fSArnaud Minier 
546*ec7d83acSArnaud Minier     for (i = 0; i < RCC_NUM_CLOCK_MUX; i++) {
547*ec7d83acSArnaud Minier         RccClockMuxState *clock_mux = &s->clock_muxes[i];
548*ec7d83acSArnaud Minier 
549*ec7d83acSArnaud Minier         if (!qdev_realize(DEVICE(clock_mux), NULL, errp)) {
550*ec7d83acSArnaud Minier             return;
551*ec7d83acSArnaud Minier         }
552*ec7d83acSArnaud Minier     }
553*ec7d83acSArnaud Minier 
554d6b55a0fSArnaud Minier     clock_update_hz(s->msi_rc, MSI_DEFAULT_FRQ);
555d6b55a0fSArnaud Minier     clock_update_hz(s->sai1_extclk, s->sai1_extclk_frequency);
556d6b55a0fSArnaud Minier     clock_update_hz(s->sai2_extclk, s->sai2_extclk_frequency);
557d6b55a0fSArnaud Minier     clock_update(s->gnd, 0);
558*ec7d83acSArnaud Minier 
559*ec7d83acSArnaud Minier     /*
560*ec7d83acSArnaud Minier      * Dummy values to make compilation pass.
561*ec7d83acSArnaud Minier      * Removed in later commits.
562*ec7d83acSArnaud Minier      */
563*ec7d83acSArnaud Minier     clock_mux_set_source(&s->clock_muxes[0], RCC_CLOCK_MUX_SRC_GND);
564*ec7d83acSArnaud Minier     clock_mux_set_enable(&s->clock_muxes[0], true);
565*ec7d83acSArnaud Minier     clock_mux_set_factor(&s->clock_muxes[0], 1, 1);
566d6b55a0fSArnaud Minier }
567d6b55a0fSArnaud Minier 
568d6b55a0fSArnaud Minier static Property stm32l4x5_rcc_properties[] = {
569d6b55a0fSArnaud Minier     DEFINE_PROP_UINT64("hse_frequency", Stm32l4x5RccState,
570d6b55a0fSArnaud Minier         hse_frequency, HSE_DEFAULT_FRQ),
571d6b55a0fSArnaud Minier     DEFINE_PROP_UINT64("sai1_extclk_frequency", Stm32l4x5RccState,
572d6b55a0fSArnaud Minier         sai1_extclk_frequency, 0),
573d6b55a0fSArnaud Minier     DEFINE_PROP_UINT64("sai2_extclk_frequency", Stm32l4x5RccState,
574d6b55a0fSArnaud Minier         sai2_extclk_frequency, 0),
575d6b55a0fSArnaud Minier     DEFINE_PROP_END_OF_LIST(),
576d6b55a0fSArnaud Minier };
577d6b55a0fSArnaud Minier 
578d6b55a0fSArnaud Minier static void stm32l4x5_rcc_class_init(ObjectClass *klass, void *data)
579d6b55a0fSArnaud Minier {
580d6b55a0fSArnaud Minier     DeviceClass *dc = DEVICE_CLASS(klass);
581d6b55a0fSArnaud Minier     ResettableClass *rc = RESETTABLE_CLASS(klass);
582d6b55a0fSArnaud Minier 
583d6b55a0fSArnaud Minier 
584d6b55a0fSArnaud Minier     rc->phases.hold = stm32l4x5_rcc_reset_hold;
585d6b55a0fSArnaud Minier     device_class_set_props(dc, stm32l4x5_rcc_properties);
586d6b55a0fSArnaud Minier     dc->realize = stm32l4x5_rcc_realize;
587d6b55a0fSArnaud Minier     dc->vmsd = &vmstate_stm32l4x5_rcc;
588d6b55a0fSArnaud Minier }
589d6b55a0fSArnaud Minier 
590d6b55a0fSArnaud Minier static const TypeInfo stm32l4x5_rcc_types[] = {
591d6b55a0fSArnaud Minier     {
592d6b55a0fSArnaud Minier         .name           = TYPE_STM32L4X5_RCC,
593d6b55a0fSArnaud Minier         .parent         = TYPE_SYS_BUS_DEVICE,
594d6b55a0fSArnaud Minier         .instance_size  = sizeof(Stm32l4x5RccState),
595d6b55a0fSArnaud Minier         .instance_init  = stm32l4x5_rcc_init,
596d6b55a0fSArnaud Minier         .class_init     = stm32l4x5_rcc_class_init,
597*ec7d83acSArnaud Minier     }, {
598*ec7d83acSArnaud Minier         .name = TYPE_RCC_CLOCK_MUX,
599*ec7d83acSArnaud Minier         .parent = TYPE_DEVICE,
600*ec7d83acSArnaud Minier         .instance_size = sizeof(RccClockMuxState),
601*ec7d83acSArnaud Minier         .instance_init = clock_mux_init,
602*ec7d83acSArnaud Minier         .class_init = clock_mux_class_init,
603d6b55a0fSArnaud Minier     }
604d6b55a0fSArnaud Minier };
605d6b55a0fSArnaud Minier 
606d6b55a0fSArnaud Minier DEFINE_TYPES(stm32l4x5_rcc_types)
607