]>
Commit | Line | Data |
---|---|---|
04a7c7b1 IV |
1 | /* |
2 | * STM32L4x5 SoC family | |
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 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
10 | * See the COPYING file in the top-level directory. | |
11 | * | |
12 | * This work is heavily inspired by the stm32f405_soc by Alistair Francis. | |
13 | * Original code is licensed under the MIT License: | |
14 | * | |
15 | * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> | |
16 | */ | |
17 | ||
18 | /* | |
19 | * The reference used is the STMicroElectronics RM0351 Reference manual | |
20 | * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. | |
21 | * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html | |
22 | */ | |
23 | ||
24 | #include "qemu/osdep.h" | |
25 | #include "qemu/units.h" | |
26 | #include "qapi/error.h" | |
27 | #include "exec/address-spaces.h" | |
28 | #include "sysemu/sysemu.h" | |
29 | #include "hw/arm/stm32l4x5_soc.h" | |
30 | #include "hw/qdev-clock.h" | |
31 | #include "hw/misc/unimp.h" | |
32 | ||
33 | #define FLASH_BASE_ADDRESS 0x08000000 | |
34 | #define SRAM1_BASE_ADDRESS 0x20000000 | |
35 | #define SRAM1_SIZE (96 * KiB) | |
36 | #define SRAM2_BASE_ADDRESS 0x10000000 | |
37 | #define SRAM2_SIZE (32 * KiB) | |
38 | ||
52671f69 | 39 | #define EXTI_ADDR 0x40010400 |
7dfe2312 | 40 | #define SYSCFG_ADDR 0x40010000 |
52671f69 IV |
41 | |
42 | #define NUM_EXTI_IRQ 40 | |
43 | /* Match exti line connections with their CPU IRQ number */ | |
44 | /* See Vector Table (Reference Manual p.396) */ | |
45 | static const int exti_irq[NUM_EXTI_IRQ] = { | |
46 | 6, /* GPIO[0] */ | |
47 | 7, /* GPIO[1] */ | |
48 | 8, /* GPIO[2] */ | |
49 | 9, /* GPIO[3] */ | |
50 | 10, /* GPIO[4] */ | |
51 | 23, 23, 23, 23, 23, /* GPIO[5..9] */ | |
52 | 40, 40, 40, 40, 40, 40, /* GPIO[10..15] */ | |
53 | 1, /* PVD */ | |
54 | 67, /* OTG_FS_WKUP, Direct */ | |
55 | 41, /* RTC_ALARM */ | |
56 | 2, /* RTC_TAMP_STAMP2/CSS_LSE */ | |
57 | 3, /* RTC wakeup timer */ | |
58 | 63, /* COMP1 */ | |
59 | 63, /* COMP2 */ | |
60 | 31, /* I2C1 wakeup, Direct */ | |
61 | 33, /* I2C2 wakeup, Direct */ | |
62 | 72, /* I2C3 wakeup, Direct */ | |
63 | 37, /* USART1 wakeup, Direct */ | |
64 | 38, /* USART2 wakeup, Direct */ | |
65 | 39, /* USART3 wakeup, Direct */ | |
66 | 52, /* UART4 wakeup, Direct */ | |
67 | 53, /* UART4 wakeup, Direct */ | |
68 | 70, /* LPUART1 wakeup, Direct */ | |
69 | 65, /* LPTIM1, Direct */ | |
70 | 66, /* LPTIM2, Direct */ | |
71 | 76, /* SWPMI1 wakeup, Direct */ | |
72 | 1, /* PVM1 wakeup */ | |
73 | 1, /* PVM2 wakeup */ | |
74 | 1, /* PVM3 wakeup */ | |
75 | 1, /* PVM4 wakeup */ | |
76 | 78 /* LCD wakeup, Direct */ | |
77 | }; | |
78 | ||
04a7c7b1 IV |
79 | static void stm32l4x5_soc_initfn(Object *obj) |
80 | { | |
81 | Stm32l4x5SocState *s = STM32L4X5_SOC(obj); | |
82 | ||
52671f69 | 83 | object_initialize_child(obj, "exti", &s->exti, TYPE_STM32L4X5_EXTI); |
7dfe2312 | 84 | object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32L4X5_SYSCFG); |
52671f69 | 85 | |
04a7c7b1 IV |
86 | s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); |
87 | s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0); | |
88 | } | |
89 | ||
90 | static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) | |
91 | { | |
92 | ERRP_GUARD(); | |
93 | Stm32l4x5SocState *s = STM32L4X5_SOC(dev_soc); | |
94 | const Stm32l4x5SocClass *sc = STM32L4X5_SOC_GET_CLASS(dev_soc); | |
95 | MemoryRegion *system_memory = get_system_memory(); | |
96 | DeviceState *armv7m; | |
52671f69 | 97 | SysBusDevice *busdev; |
04a7c7b1 IV |
98 | |
99 | /* | |
100 | * We use s->refclk internally and only define it with qdev_init_clock_in() | |
101 | * so it is correctly parented and not leaked on an init/deinit; it is not | |
102 | * intended as an externally exposed clock. | |
103 | */ | |
104 | if (clock_has_source(s->refclk)) { | |
105 | error_setg(errp, "refclk clock must not be wired up by the board code"); | |
106 | return; | |
107 | } | |
108 | ||
109 | if (!clock_has_source(s->sysclk)) { | |
110 | error_setg(errp, "sysclk clock must be wired up by the board code"); | |
111 | return; | |
112 | } | |
113 | ||
114 | /* | |
115 | * TODO: ideally we should model the SoC RCC and its ability to | |
116 | * change the sysclk frequency and define different sysclk sources. | |
117 | */ | |
118 | ||
119 | /* The refclk always runs at frequency HCLK / 8 */ | |
120 | clock_set_mul_div(s->refclk, 8, 1); | |
121 | clock_set_source(s->refclk, s->sysclk); | |
122 | ||
123 | if (!memory_region_init_rom(&s->flash, OBJECT(dev_soc), "flash", | |
124 | sc->flash_size, errp)) { | |
125 | return; | |
126 | } | |
127 | memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc), | |
128 | "flash_boot_alias", &s->flash, 0, | |
129 | sc->flash_size); | |
130 | ||
131 | memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash); | |
132 | memory_region_add_subregion(system_memory, 0, &s->flash_alias); | |
133 | ||
134 | if (!memory_region_init_ram(&s->sram1, OBJECT(dev_soc), "SRAM1", SRAM1_SIZE, | |
135 | errp)) { | |
136 | return; | |
137 | } | |
138 | memory_region_add_subregion(system_memory, SRAM1_BASE_ADDRESS, &s->sram1); | |
139 | ||
140 | if (!memory_region_init_ram(&s->sram2, OBJECT(dev_soc), "SRAM2", SRAM2_SIZE, | |
141 | errp)) { | |
142 | return; | |
143 | } | |
144 | memory_region_add_subregion(system_memory, SRAM2_BASE_ADDRESS, &s->sram2); | |
145 | ||
146 | object_initialize_child(OBJECT(dev_soc), "armv7m", &s->armv7m, TYPE_ARMV7M); | |
147 | armv7m = DEVICE(&s->armv7m); | |
148 | qdev_prop_set_uint32(armv7m, "num-irq", 96); | |
4a04655c | 149 | qdev_prop_set_uint32(armv7m, "num-prio-bits", 4); |
04a7c7b1 IV |
150 | qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); |
151 | qdev_prop_set_bit(armv7m, "enable-bitband", true); | |
152 | qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); | |
153 | qdev_connect_clock_in(armv7m, "refclk", s->refclk); | |
154 | object_property_set_link(OBJECT(&s->armv7m), "memory", | |
155 | OBJECT(system_memory), &error_abort); | |
156 | if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { | |
157 | return; | |
158 | } | |
159 | ||
7dfe2312 IV |
160 | /* System configuration controller */ |
161 | busdev = SYS_BUS_DEVICE(&s->syscfg); | |
162 | if (!sysbus_realize(busdev, errp)) { | |
163 | return; | |
164 | } | |
165 | sysbus_mmio_map(busdev, 0, SYSCFG_ADDR); | |
166 | /* | |
167 | * TODO: when the GPIO device is implemented, connect it | |
168 | * to SYCFG using `qdev_connect_gpio_out`, NUM_GPIOS and | |
169 | * GPIO_NUM_PINS. | |
170 | */ | |
171 | ||
172 | /* EXTI device */ | |
52671f69 IV |
173 | busdev = SYS_BUS_DEVICE(&s->exti); |
174 | if (!sysbus_realize(busdev, errp)) { | |
175 | return; | |
176 | } | |
177 | sysbus_mmio_map(busdev, 0, EXTI_ADDR); | |
178 | for (unsigned i = 0; i < NUM_EXTI_IRQ; i++) { | |
179 | sysbus_connect_irq(busdev, i, qdev_get_gpio_in(armv7m, exti_irq[i])); | |
180 | } | |
181 | ||
7dfe2312 IV |
182 | for (unsigned i = 0; i < 16; i++) { |
183 | qdev_connect_gpio_out(DEVICE(&s->syscfg), i, | |
184 | qdev_get_gpio_in(DEVICE(&s->exti), i)); | |
185 | } | |
186 | ||
04a7c7b1 IV |
187 | /* APB1 BUS */ |
188 | create_unimplemented_device("TIM2", 0x40000000, 0x400); | |
189 | create_unimplemented_device("TIM3", 0x40000400, 0x400); | |
190 | create_unimplemented_device("TIM4", 0x40000800, 0x400); | |
191 | create_unimplemented_device("TIM5", 0x40000C00, 0x400); | |
192 | create_unimplemented_device("TIM6", 0x40001000, 0x400); | |
193 | create_unimplemented_device("TIM7", 0x40001400, 0x400); | |
194 | /* RESERVED: 0x40001800, 0x1000 */ | |
195 | create_unimplemented_device("RTC", 0x40002800, 0x400); | |
196 | create_unimplemented_device("WWDG", 0x40002C00, 0x400); | |
197 | create_unimplemented_device("IWDG", 0x40003000, 0x400); | |
198 | /* RESERVED: 0x40001800, 0x400 */ | |
199 | create_unimplemented_device("SPI2", 0x40003800, 0x400); | |
200 | create_unimplemented_device("SPI3", 0x40003C00, 0x400); | |
201 | /* RESERVED: 0x40004000, 0x400 */ | |
202 | create_unimplemented_device("USART2", 0x40004400, 0x400); | |
203 | create_unimplemented_device("USART3", 0x40004800, 0x400); | |
204 | create_unimplemented_device("UART4", 0x40004C00, 0x400); | |
205 | create_unimplemented_device("UART5", 0x40005000, 0x400); | |
206 | create_unimplemented_device("I2C1", 0x40005400, 0x400); | |
207 | create_unimplemented_device("I2C2", 0x40005800, 0x400); | |
208 | create_unimplemented_device("I2C3", 0x40005C00, 0x400); | |
209 | /* RESERVED: 0x40006000, 0x400 */ | |
210 | create_unimplemented_device("CAN1", 0x40006400, 0x400); | |
211 | /* RESERVED: 0x40006800, 0x400 */ | |
212 | create_unimplemented_device("PWR", 0x40007000, 0x400); | |
213 | create_unimplemented_device("DAC1", 0x40007400, 0x400); | |
214 | create_unimplemented_device("OPAMP", 0x40007800, 0x400); | |
215 | create_unimplemented_device("LPTIM1", 0x40007C00, 0x400); | |
216 | create_unimplemented_device("LPUART1", 0x40008000, 0x400); | |
217 | /* RESERVED: 0x40008400, 0x400 */ | |
218 | create_unimplemented_device("SWPMI1", 0x40008800, 0x400); | |
219 | /* RESERVED: 0x40008C00, 0x800 */ | |
220 | create_unimplemented_device("LPTIM2", 0x40009400, 0x400); | |
221 | /* RESERVED: 0x40009800, 0x6800 */ | |
222 | ||
223 | /* APB2 BUS */ | |
04a7c7b1 IV |
224 | create_unimplemented_device("VREFBUF", 0x40010030, 0x1D0); |
225 | create_unimplemented_device("COMP", 0x40010200, 0x200); | |
04a7c7b1 IV |
226 | /* RESERVED: 0x40010800, 0x1400 */ |
227 | create_unimplemented_device("FIREWALL", 0x40011C00, 0x400); | |
228 | /* RESERVED: 0x40012000, 0x800 */ | |
229 | create_unimplemented_device("SDMMC1", 0x40012800, 0x400); | |
230 | create_unimplemented_device("TIM1", 0x40012C00, 0x400); | |
231 | create_unimplemented_device("SPI1", 0x40013000, 0x400); | |
232 | create_unimplemented_device("TIM8", 0x40013400, 0x400); | |
233 | create_unimplemented_device("USART1", 0x40013800, 0x400); | |
234 | /* RESERVED: 0x40013C00, 0x400 */ | |
235 | create_unimplemented_device("TIM15", 0x40014000, 0x400); | |
236 | create_unimplemented_device("TIM16", 0x40014400, 0x400); | |
237 | create_unimplemented_device("TIM17", 0x40014800, 0x400); | |
238 | /* RESERVED: 0x40014C00, 0x800 */ | |
239 | create_unimplemented_device("SAI1", 0x40015400, 0x400); | |
240 | create_unimplemented_device("SAI2", 0x40015800, 0x400); | |
241 | /* RESERVED: 0x40015C00, 0x400 */ | |
242 | create_unimplemented_device("DFSDM1", 0x40016000, 0x400); | |
243 | /* RESERVED: 0x40016400, 0x9C00 */ | |
244 | ||
245 | /* AHB1 BUS */ | |
246 | create_unimplemented_device("DMA1", 0x40020000, 0x400); | |
247 | create_unimplemented_device("DMA2", 0x40020400, 0x400); | |
248 | /* RESERVED: 0x40020800, 0x800 */ | |
249 | create_unimplemented_device("RCC", 0x40021000, 0x400); | |
250 | /* RESERVED: 0x40021400, 0xC00 */ | |
251 | create_unimplemented_device("FLASH", 0x40022000, 0x400); | |
252 | /* RESERVED: 0x40022400, 0xC00 */ | |
253 | create_unimplemented_device("CRC", 0x40023000, 0x400); | |
254 | /* RESERVED: 0x40023400, 0x400 */ | |
255 | create_unimplemented_device("TSC", 0x40024000, 0x400); | |
256 | ||
257 | /* RESERVED: 0x40024400, 0x7FDBC00 */ | |
258 | ||
259 | /* AHB2 BUS */ | |
260 | create_unimplemented_device("GPIOA", 0x48000000, 0x400); | |
261 | create_unimplemented_device("GPIOB", 0x48000400, 0x400); | |
262 | create_unimplemented_device("GPIOC", 0x48000800, 0x400); | |
263 | create_unimplemented_device("GPIOD", 0x48000C00, 0x400); | |
264 | create_unimplemented_device("GPIOE", 0x48001000, 0x400); | |
265 | create_unimplemented_device("GPIOF", 0x48001400, 0x400); | |
266 | create_unimplemented_device("GPIOG", 0x48001800, 0x400); | |
267 | create_unimplemented_device("GPIOH", 0x48001C00, 0x400); | |
268 | /* RESERVED: 0x48002000, 0x7FDBC00 */ | |
269 | create_unimplemented_device("OTG_FS", 0x50000000, 0x40000); | |
270 | create_unimplemented_device("ADC", 0x50040000, 0x400); | |
271 | /* RESERVED: 0x50040400, 0x20400 */ | |
272 | create_unimplemented_device("RNG", 0x50060800, 0x400); | |
273 | ||
274 | /* AHB3 BUS */ | |
275 | create_unimplemented_device("FMC", 0xA0000000, 0x1000); | |
276 | create_unimplemented_device("QUADSPI", 0xA0001000, 0x400); | |
277 | } | |
278 | ||
279 | static void stm32l4x5_soc_class_init(ObjectClass *klass, void *data) | |
280 | { | |
281 | ||
282 | DeviceClass *dc = DEVICE_CLASS(klass); | |
283 | ||
284 | dc->realize = stm32l4x5_soc_realize; | |
285 | /* Reason: Mapped at fixed location on the system bus */ | |
286 | dc->user_creatable = false; | |
287 | /* No vmstate or reset required: device has no internal state */ | |
288 | } | |
289 | ||
290 | static void stm32l4x5xc_soc_class_init(ObjectClass *oc, void *data) | |
291 | { | |
292 | Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); | |
293 | ||
294 | ssc->flash_size = 256 * KiB; | |
295 | } | |
296 | ||
297 | static void stm32l4x5xe_soc_class_init(ObjectClass *oc, void *data) | |
298 | { | |
299 | Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); | |
300 | ||
301 | ssc->flash_size = 512 * KiB; | |
302 | } | |
303 | ||
304 | static void stm32l4x5xg_soc_class_init(ObjectClass *oc, void *data) | |
305 | { | |
306 | Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); | |
307 | ||
308 | ssc->flash_size = 1 * MiB; | |
309 | } | |
310 | ||
311 | static const TypeInfo stm32l4x5_soc_types[] = { | |
312 | { | |
313 | .name = TYPE_STM32L4X5XC_SOC, | |
314 | .parent = TYPE_STM32L4X5_SOC, | |
315 | .class_init = stm32l4x5xc_soc_class_init, | |
316 | }, { | |
317 | .name = TYPE_STM32L4X5XE_SOC, | |
318 | .parent = TYPE_STM32L4X5_SOC, | |
319 | .class_init = stm32l4x5xe_soc_class_init, | |
320 | }, { | |
321 | .name = TYPE_STM32L4X5XG_SOC, | |
322 | .parent = TYPE_STM32L4X5_SOC, | |
323 | .class_init = stm32l4x5xg_soc_class_init, | |
324 | }, { | |
325 | .name = TYPE_STM32L4X5_SOC, | |
326 | .parent = TYPE_SYS_BUS_DEVICE, | |
327 | .instance_size = sizeof(Stm32l4x5SocState), | |
328 | .instance_init = stm32l4x5_soc_initfn, | |
329 | .class_size = sizeof(Stm32l4x5SocClass), | |
330 | .class_init = stm32l4x5_soc_class_init, | |
331 | .abstract = true, | |
332 | } | |
333 | }; | |
334 | ||
335 | DEFINE_TYPES(stm32l4x5_soc_types) |