| 1 | /* |
| 2 | * STM32L4x5 SoC family |
| 3 | * |
| 4 | * Copyright (c) 2023-2024 Arnaud Minier <arnaud.minier@telecom-paris.fr> |
| 5 | * Copyright (c) 2023-2024 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 "system/address-spaces.h" |
| 28 | #include "system/system.h" |
| 29 | #include "hw/core/or-irq.h" |
| 30 | #include "hw/arm/stm32l4x5_soc.h" |
| 31 | #include "hw/char/stm32l4x5_usart.h" |
| 32 | #include "hw/gpio/stm32l4x5_gpio.h" |
| 33 | #include "hw/core/qdev-clock.h" |
| 34 | #include "hw/misc/unimp.h" |
| 35 | |
| 36 | #define FLASH_BASE_ADDRESS 0x08000000 |
| 37 | #define SRAM1_BASE_ADDRESS 0x20000000 |
| 38 | #define SRAM1_SIZE (96 * KiB) |
| 39 | #define SRAM2_BASE_ADDRESS 0x10000000 |
| 40 | #define SRAM2_SIZE (32 * KiB) |
| 41 | |
| 42 | #define EXTI_ADDR 0x40010400 |
| 43 | #define SYSCFG_ADDR 0x40010000 |
| 44 | |
| 45 | #define NUM_EXTI_IRQ 40 |
| 46 | /* Match exti line connections with their CPU IRQ number */ |
| 47 | /* See Vector Table (Reference Manual p.396) */ |
| 48 | /* |
| 49 | * Some IRQs are connected to the same CPU IRQ (denoted by -1) |
| 50 | * and require an intermediary OR gate to function correctly. |
| 51 | */ |
| 52 | static const int exti_irq[NUM_EXTI_IRQ] = { |
| 53 | 6, /* GPIO[0] */ |
| 54 | 7, /* GPIO[1] */ |
| 55 | 8, /* GPIO[2] */ |
| 56 | 9, /* GPIO[3] */ |
| 57 | 10, /* GPIO[4] */ |
| 58 | -1, -1, -1, -1, -1, /* GPIO[5..9] OR gate 23 */ |
| 59 | -1, -1, -1, -1, -1, -1, /* GPIO[10..15] OR gate 40 */ |
| 60 | -1, /* PVD OR gate 1 */ |
| 61 | 67, /* OTG_FS_WKUP, Direct */ |
| 62 | 41, /* RTC_ALARM */ |
| 63 | 2, /* RTC_TAMP_STAMP2/CSS_LSE */ |
| 64 | 3, /* RTC wakeup timer */ |
| 65 | -1, -1, /* COMP[1..2] OR gate 63 */ |
| 66 | 31, /* I2C1 wakeup, Direct */ |
| 67 | 33, /* I2C2 wakeup, Direct */ |
| 68 | 72, /* I2C3 wakeup, Direct */ |
| 69 | 37, /* USART1 wakeup, Direct */ |
| 70 | 38, /* USART2 wakeup, Direct */ |
| 71 | 39, /* USART3 wakeup, Direct */ |
| 72 | 52, /* UART4 wakeup, Direct */ |
| 73 | 53, /* UART4 wakeup, Direct */ |
| 74 | 70, /* LPUART1 wakeup, Direct */ |
| 75 | 65, /* LPTIM1, Direct */ |
| 76 | 66, /* LPTIM2, Direct */ |
| 77 | 76, /* SWPMI1 wakeup, Direct */ |
| 78 | -1, -1, -1, -1, /* PVM[1..4] OR gate 1 */ |
| 79 | 78 /* LCD wakeup, Direct */ |
| 80 | }; |
| 81 | #define RCC_BASE_ADDRESS 0x40021000 |
| 82 | #define RCC_IRQ 5 |
| 83 | |
| 84 | #define EXTI_USART1_IRQ 26 |
| 85 | #define EXTI_UART4_IRQ 29 |
| 86 | #define EXTI_LPUART1_IRQ 31 |
| 87 | |
| 88 | static const int exti_or_gates_out[NUM_EXTI_OR_GATES] = { |
| 89 | 23, 40, 63, 1, |
| 90 | }; |
| 91 | |
| 92 | static const int exti_or_gates_num_lines_in[NUM_EXTI_OR_GATES] = { |
| 93 | 5, 6, 2, 5, |
| 94 | }; |
| 95 | |
| 96 | /* 3 OR gates with consecutive inputs */ |
| 97 | #define NUM_EXTI_SIMPLE_OR_GATES 3 |
| 98 | static const int exti_or_gates_first_line_in[NUM_EXTI_SIMPLE_OR_GATES] = { |
| 99 | 5, 10, 21, |
| 100 | }; |
| 101 | |
| 102 | /* 1 OR gate with non-consecutive inputs */ |
| 103 | #define EXTI_OR_GATE1_NUM_LINES_IN 5 |
| 104 | static const int exti_or_gate1_lines_in[EXTI_OR_GATE1_NUM_LINES_IN] = { |
| 105 | 16, 35, 36, 37, 38, |
| 106 | }; |
| 107 | |
| 108 | static const struct { |
| 109 | uint32_t addr; |
| 110 | uint32_t moder_reset; |
| 111 | uint32_t ospeedr_reset; |
| 112 | uint32_t pupdr_reset; |
| 113 | } stm32l4x5_gpio_cfg[NUM_GPIOS] = { |
| 114 | { 0x48000000, 0xABFFFFFF, 0x0C000000, 0x64000000 }, |
| 115 | { 0x48000400, 0xFFFFFEBF, 0x00000000, 0x00000100 }, |
| 116 | { 0x48000800, 0xFFFFFFFF, 0x00000000, 0x00000000 }, |
| 117 | { 0x48000C00, 0xFFFFFFFF, 0x00000000, 0x00000000 }, |
| 118 | { 0x48001000, 0xFFFFFFFF, 0x00000000, 0x00000000 }, |
| 119 | { 0x48001400, 0xFFFFFFFF, 0x00000000, 0x00000000 }, |
| 120 | { 0x48001800, 0xFFFFFFFF, 0x00000000, 0x00000000 }, |
| 121 | { 0x48001C00, 0x0000000F, 0x00000000, 0x00000000 }, |
| 122 | }; |
| 123 | |
| 124 | static const hwaddr usart_addr[] = { |
| 125 | 0x40013800, /* "USART1", 0x400 */ |
| 126 | 0x40004400, /* "USART2", 0x400 */ |
| 127 | 0x40004800, /* "USART3", 0x400 */ |
| 128 | }; |
| 129 | static const hwaddr uart_addr[] = { |
| 130 | 0x40004C00, /* "UART4" , 0x400 */ |
| 131 | 0x40005000 /* "UART5" , 0x400 */ |
| 132 | }; |
| 133 | |
| 134 | #define LPUART_BASE_ADDRESS 0x40008000 |
| 135 | |
| 136 | static void stm32l4x5_soc_initfn(Object *obj) |
| 137 | { |
| 138 | Stm32l4x5SocState *s = STM32L4X5_SOC(obj); |
| 139 | |
| 140 | object_initialize_child(obj, "exti", &s->exti, TYPE_STM32L4X5_EXTI); |
| 141 | for (unsigned i = 0; i < NUM_EXTI_OR_GATES; i++) { |
| 142 | object_initialize_child(obj, "exti_or_gates[*]", &s->exti_or_gates[i], |
| 143 | TYPE_OR_IRQ); |
| 144 | } |
| 145 | object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32L4X5_SYSCFG); |
| 146 | object_initialize_child(obj, "rcc", &s->rcc, TYPE_STM32L4X5_RCC); |
| 147 | |
| 148 | for (unsigned i = 0; i < NUM_GPIOS; i++) { |
| 149 | g_autofree char *name = g_strdup_printf("gpio%c", 'a' + i); |
| 150 | object_initialize_child(obj, name, &s->gpio[i], TYPE_STM32L4X5_GPIO); |
| 151 | } |
| 152 | |
| 153 | for (int i = 0; i < STM_NUM_USARTS; i++) { |
| 154 | object_initialize_child(obj, "usart[*]", &s->usart[i], |
| 155 | TYPE_STM32L4X5_USART); |
| 156 | } |
| 157 | |
| 158 | for (int i = 0; i < STM_NUM_UARTS; i++) { |
| 159 | object_initialize_child(obj, "uart[*]", &s->uart[i], |
| 160 | TYPE_STM32L4X5_UART); |
| 161 | } |
| 162 | object_initialize_child(obj, "lpuart1", &s->lpuart, |
| 163 | TYPE_STM32L4X5_LPUART); |
| 164 | } |
| 165 | |
| 166 | static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) |
| 167 | { |
| 168 | ERRP_GUARD(); |
| 169 | Stm32l4x5SocState *s = STM32L4X5_SOC(dev_soc); |
| 170 | const Stm32l4x5SocClass *sc = STM32L4X5_SOC_GET_CLASS(dev_soc); |
| 171 | MemoryRegion *system_memory = get_system_memory(); |
| 172 | DeviceState *armv7m, *dev; |
| 173 | SysBusDevice *busdev; |
| 174 | uint32_t pin_index; |
| 175 | |
| 176 | if (!memory_region_init_rom(&s->flash, OBJECT(dev_soc), "flash", |
| 177 | sc->flash_size, errp)) { |
| 178 | return; |
| 179 | } |
| 180 | memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc), |
| 181 | "flash_boot_alias", &s->flash, 0, |
| 182 | sc->flash_size); |
| 183 | |
| 184 | memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash); |
| 185 | memory_region_add_subregion(system_memory, 0, &s->flash_alias); |
| 186 | |
| 187 | if (!memory_region_init_ram(&s->sram1, OBJECT(dev_soc), "SRAM1", SRAM1_SIZE, |
| 188 | errp)) { |
| 189 | return; |
| 190 | } |
| 191 | memory_region_add_subregion(system_memory, SRAM1_BASE_ADDRESS, &s->sram1); |
| 192 | |
| 193 | if (!memory_region_init_ram(&s->sram2, OBJECT(dev_soc), "SRAM2", SRAM2_SIZE, |
| 194 | errp)) { |
| 195 | return; |
| 196 | } |
| 197 | memory_region_add_subregion(system_memory, SRAM2_BASE_ADDRESS, &s->sram2); |
| 198 | |
| 199 | object_initialize_child(OBJECT(dev_soc), "armv7m", &s->armv7m, TYPE_ARMV7M); |
| 200 | armv7m = DEVICE(&s->armv7m); |
| 201 | qdev_prop_set_uint32(armv7m, "num-irq", 96); |
| 202 | qdev_prop_set_uint32(armv7m, "num-prio-bits", 4); |
| 203 | qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); |
| 204 | qdev_prop_set_bit(armv7m, "enable-bitband", true); |
| 205 | qdev_connect_clock_in(armv7m, "cpuclk", |
| 206 | qdev_get_clock_out(DEVICE(&(s->rcc)), "cortex-fclk-out")); |
| 207 | qdev_connect_clock_in(armv7m, "refclk", |
| 208 | qdev_get_clock_out(DEVICE(&(s->rcc)), "cortex-refclk-out")); |
| 209 | object_property_set_link(OBJECT(&s->armv7m), "memory", |
| 210 | OBJECT(system_memory), &error_abort); |
| 211 | if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { |
| 212 | return; |
| 213 | } |
| 214 | |
| 215 | /* GPIOs */ |
| 216 | for (unsigned i = 0; i < NUM_GPIOS; i++) { |
| 217 | g_autofree char *name = g_strdup_printf("%c", 'A' + i); |
| 218 | dev = DEVICE(&s->gpio[i]); |
| 219 | qdev_prop_set_string(dev, "name", name); |
| 220 | qdev_prop_set_uint32(dev, "mode-reset", |
| 221 | stm32l4x5_gpio_cfg[i].moder_reset); |
| 222 | qdev_prop_set_uint32(dev, "ospeed-reset", |
| 223 | stm32l4x5_gpio_cfg[i].ospeedr_reset); |
| 224 | qdev_prop_set_uint32(dev, "pupd-reset", |
| 225 | stm32l4x5_gpio_cfg[i].pupdr_reset); |
| 226 | busdev = SYS_BUS_DEVICE(&s->gpio[i]); |
| 227 | g_free(name); |
| 228 | name = g_strdup_printf("gpio%c-out", 'a' + i); |
| 229 | qdev_connect_clock_in(DEVICE(&s->gpio[i]), "clk", |
| 230 | qdev_get_clock_out(DEVICE(&(s->rcc)), name)); |
| 231 | if (!sysbus_realize(busdev, errp)) { |
| 232 | return; |
| 233 | } |
| 234 | sysbus_mmio_map(busdev, 0, stm32l4x5_gpio_cfg[i].addr); |
| 235 | } |
| 236 | |
| 237 | /* System configuration controller */ |
| 238 | busdev = SYS_BUS_DEVICE(&s->syscfg); |
| 239 | qdev_connect_clock_in(DEVICE(&s->syscfg), "clk", |
| 240 | qdev_get_clock_out(DEVICE(&(s->rcc)), "syscfg-out")); |
| 241 | if (!sysbus_realize(busdev, errp)) { |
| 242 | return; |
| 243 | } |
| 244 | sysbus_mmio_map(busdev, 0, SYSCFG_ADDR); |
| 245 | |
| 246 | for (unsigned i = 0; i < NUM_GPIOS; i++) { |
| 247 | for (unsigned j = 0; j < GPIO_NUM_PINS; j++) { |
| 248 | pin_index = GPIO_NUM_PINS * i + j; |
| 249 | qdev_connect_gpio_out(DEVICE(&s->gpio[i]), j, |
| 250 | qdev_get_gpio_in(DEVICE(&s->syscfg), |
| 251 | pin_index)); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | qdev_pass_gpios(DEVICE(&s->syscfg), dev_soc, NULL); |
| 256 | |
| 257 | /* EXTI device */ |
| 258 | busdev = SYS_BUS_DEVICE(&s->exti); |
| 259 | if (!sysbus_realize(busdev, errp)) { |
| 260 | return; |
| 261 | } |
| 262 | sysbus_mmio_map(busdev, 0, EXTI_ADDR); |
| 263 | |
| 264 | /* IRQs with fan-in that require an OR gate */ |
| 265 | for (unsigned i = 0; i < NUM_EXTI_OR_GATES; i++) { |
| 266 | if (!object_property_set_int(OBJECT(&s->exti_or_gates[i]), "num-lines", |
| 267 | exti_or_gates_num_lines_in[i], errp)) { |
| 268 | return; |
| 269 | } |
| 270 | if (!qdev_realize(DEVICE(&s->exti_or_gates[i]), NULL, errp)) { |
| 271 | return; |
| 272 | } |
| 273 | |
| 274 | qdev_connect_gpio_out(DEVICE(&s->exti_or_gates[i]), 0, |
| 275 | qdev_get_gpio_in(armv7m, exti_or_gates_out[i])); |
| 276 | |
| 277 | if (i < NUM_EXTI_SIMPLE_OR_GATES) { |
| 278 | /* consecutive inputs for OR gates 23, 40, 63 */ |
| 279 | for (unsigned j = 0; j < exti_or_gates_num_lines_in[i]; j++) { |
| 280 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->exti), |
| 281 | exti_or_gates_first_line_in[i] + j, |
| 282 | qdev_get_gpio_in(DEVICE(&s->exti_or_gates[i]), j)); |
| 283 | } |
| 284 | } else { |
| 285 | /* non-consecutive inputs for OR gate 1 */ |
| 286 | for (unsigned j = 0; j < EXTI_OR_GATE1_NUM_LINES_IN; j++) { |
| 287 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->exti), |
| 288 | exti_or_gate1_lines_in[j], |
| 289 | qdev_get_gpio_in(DEVICE(&s->exti_or_gates[i]), j)); |
| 290 | } |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | /* IRQs that don't require fan-in */ |
| 295 | for (unsigned i = 0; i < NUM_EXTI_IRQ; i++) { |
| 296 | if (exti_irq[i] != -1) { |
| 297 | sysbus_connect_irq(busdev, i, |
| 298 | qdev_get_gpio_in(armv7m, exti_irq[i])); |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | /* Connect SYSCFG to EXTI */ |
| 303 | for (unsigned i = 0; i < GPIO_NUM_PINS; i++) { |
| 304 | qdev_connect_gpio_out(DEVICE(&s->syscfg), i, |
| 305 | qdev_get_gpio_in(DEVICE(&s->exti), i)); |
| 306 | } |
| 307 | |
| 308 | /* RCC device */ |
| 309 | busdev = SYS_BUS_DEVICE(&s->rcc); |
| 310 | if (!sysbus_realize(busdev, errp)) { |
| 311 | return; |
| 312 | } |
| 313 | sysbus_mmio_map(busdev, 0, RCC_BASE_ADDRESS); |
| 314 | sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, RCC_IRQ)); |
| 315 | |
| 316 | /* USART devices */ |
| 317 | for (int i = 0; i < STM_NUM_USARTS; i++) { |
| 318 | g_autofree char *name = g_strdup_printf("usart%d-out", i + 1); |
| 319 | dev = DEVICE(&(s->usart[i])); |
| 320 | qdev_prop_set_chr(dev, "chardev", serial_hd(i)); |
| 321 | qdev_connect_clock_in(dev, "clk", |
| 322 | qdev_get_clock_out(DEVICE(&(s->rcc)), name)); |
| 323 | busdev = SYS_BUS_DEVICE(dev); |
| 324 | if (!sysbus_realize(busdev, errp)) { |
| 325 | return; |
| 326 | } |
| 327 | sysbus_mmio_map(busdev, 0, usart_addr[i]); |
| 328 | sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->exti), |
| 329 | EXTI_USART1_IRQ + i)); |
| 330 | } |
| 331 | |
| 332 | /* UART devices */ |
| 333 | for (int i = 0; i < STM_NUM_UARTS; i++) { |
| 334 | g_autofree char *name = g_strdup_printf("uart%d-out", STM_NUM_USARTS + i + 1); |
| 335 | dev = DEVICE(&(s->uart[i])); |
| 336 | qdev_prop_set_chr(dev, "chardev", serial_hd(STM_NUM_USARTS + i)); |
| 337 | qdev_connect_clock_in(dev, "clk", |
| 338 | qdev_get_clock_out(DEVICE(&(s->rcc)), name)); |
| 339 | busdev = SYS_BUS_DEVICE(dev); |
| 340 | if (!sysbus_realize(busdev, errp)) { |
| 341 | return; |
| 342 | } |
| 343 | sysbus_mmio_map(busdev, 0, uart_addr[i]); |
| 344 | sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->exti), |
| 345 | EXTI_UART4_IRQ + i)); |
| 346 | } |
| 347 | |
| 348 | /* LPUART device*/ |
| 349 | dev = DEVICE(&(s->lpuart)); |
| 350 | qdev_prop_set_chr(dev, "chardev", serial_hd(STM_NUM_USARTS + STM_NUM_UARTS)); |
| 351 | qdev_connect_clock_in(dev, "clk", |
| 352 | qdev_get_clock_out(DEVICE(&(s->rcc)), "lpuart1-out")); |
| 353 | busdev = SYS_BUS_DEVICE(dev); |
| 354 | if (!sysbus_realize(busdev, errp)) { |
| 355 | return; |
| 356 | } |
| 357 | sysbus_mmio_map(busdev, 0, LPUART_BASE_ADDRESS); |
| 358 | sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->exti), |
| 359 | EXTI_LPUART1_IRQ)); |
| 360 | |
| 361 | /* APB1 BUS */ |
| 362 | create_unimplemented_device("TIM2", 0x40000000, 0x400); |
| 363 | create_unimplemented_device("TIM3", 0x40000400, 0x400); |
| 364 | create_unimplemented_device("TIM4", 0x40000800, 0x400); |
| 365 | create_unimplemented_device("TIM5", 0x40000C00, 0x400); |
| 366 | create_unimplemented_device("TIM6", 0x40001000, 0x400); |
| 367 | create_unimplemented_device("TIM7", 0x40001400, 0x400); |
| 368 | /* RESERVED: 0x40001800, 0x1000 */ |
| 369 | create_unimplemented_device("RTC", 0x40002800, 0x400); |
| 370 | create_unimplemented_device("WWDG", 0x40002C00, 0x400); |
| 371 | create_unimplemented_device("IWDG", 0x40003000, 0x400); |
| 372 | /* RESERVED: 0x40001800, 0x400 */ |
| 373 | create_unimplemented_device("SPI2", 0x40003800, 0x400); |
| 374 | create_unimplemented_device("SPI3", 0x40003C00, 0x400); |
| 375 | /* RESERVED: 0x40004000, 0x400 */ |
| 376 | create_unimplemented_device("I2C1", 0x40005400, 0x400); |
| 377 | create_unimplemented_device("I2C2", 0x40005800, 0x400); |
| 378 | create_unimplemented_device("I2C3", 0x40005C00, 0x400); |
| 379 | /* RESERVED: 0x40006000, 0x400 */ |
| 380 | create_unimplemented_device("CAN1", 0x40006400, 0x400); |
| 381 | /* RESERVED: 0x40006800, 0x400 */ |
| 382 | create_unimplemented_device("PWR", 0x40007000, 0x400); |
| 383 | create_unimplemented_device("DAC1", 0x40007400, 0x400); |
| 384 | create_unimplemented_device("OPAMP", 0x40007800, 0x400); |
| 385 | create_unimplemented_device("LPTIM1", 0x40007C00, 0x400); |
| 386 | /* RESERVED: 0x40008400, 0x400 */ |
| 387 | create_unimplemented_device("SWPMI1", 0x40008800, 0x400); |
| 388 | /* RESERVED: 0x40008C00, 0x800 */ |
| 389 | create_unimplemented_device("LPTIM2", 0x40009400, 0x400); |
| 390 | /* RESERVED: 0x40009800, 0x6800 */ |
| 391 | |
| 392 | /* APB2 BUS */ |
| 393 | create_unimplemented_device("VREFBUF", 0x40010030, 0x1D0); |
| 394 | create_unimplemented_device("COMP", 0x40010200, 0x200); |
| 395 | /* RESERVED: 0x40010800, 0x1400 */ |
| 396 | create_unimplemented_device("FIREWALL", 0x40011C00, 0x400); |
| 397 | /* RESERVED: 0x40012000, 0x800 */ |
| 398 | create_unimplemented_device("SDMMC1", 0x40012800, 0x400); |
| 399 | create_unimplemented_device("TIM1", 0x40012C00, 0x400); |
| 400 | create_unimplemented_device("SPI1", 0x40013000, 0x400); |
| 401 | create_unimplemented_device("TIM8", 0x40013400, 0x400); |
| 402 | /* RESERVED: 0x40013C00, 0x400 */ |
| 403 | create_unimplemented_device("TIM15", 0x40014000, 0x400); |
| 404 | create_unimplemented_device("TIM16", 0x40014400, 0x400); |
| 405 | create_unimplemented_device("TIM17", 0x40014800, 0x400); |
| 406 | /* RESERVED: 0x40014C00, 0x800 */ |
| 407 | create_unimplemented_device("SAI1", 0x40015400, 0x400); |
| 408 | create_unimplemented_device("SAI2", 0x40015800, 0x400); |
| 409 | /* RESERVED: 0x40015C00, 0x400 */ |
| 410 | create_unimplemented_device("DFSDM1", 0x40016000, 0x400); |
| 411 | /* RESERVED: 0x40016400, 0x9C00 */ |
| 412 | |
| 413 | /* AHB1 BUS */ |
| 414 | create_unimplemented_device("DMA1", 0x40020000, 0x400); |
| 415 | create_unimplemented_device("DMA2", 0x40020400, 0x400); |
| 416 | /* RESERVED: 0x40020800, 0x800 */ |
| 417 | /* RESERVED: 0x40021400, 0xC00 */ |
| 418 | create_unimplemented_device("FLASH", 0x40022000, 0x400); |
| 419 | /* RESERVED: 0x40022400, 0xC00 */ |
| 420 | create_unimplemented_device("CRC", 0x40023000, 0x400); |
| 421 | /* RESERVED: 0x40023400, 0x400 */ |
| 422 | create_unimplemented_device("TSC", 0x40024000, 0x400); |
| 423 | |
| 424 | /* RESERVED: 0x40024400, 0x7FDBC00 */ |
| 425 | |
| 426 | /* AHB2 BUS */ |
| 427 | /* RESERVED: 0x48002000, 0x7FDBC00 */ |
| 428 | create_unimplemented_device("OTG_FS", 0x50000000, 0x40000); |
| 429 | create_unimplemented_device("ADC", 0x50040000, 0x400); |
| 430 | /* RESERVED: 0x50040400, 0x20400 */ |
| 431 | create_unimplemented_device("RNG", 0x50060800, 0x400); |
| 432 | |
| 433 | /* AHB3 BUS */ |
| 434 | create_unimplemented_device("FMC", 0xA0000000, 0x1000); |
| 435 | create_unimplemented_device("QUADSPI", 0xA0001000, 0x400); |
| 436 | } |
| 437 | |
| 438 | static void stm32l4x5_soc_class_init(ObjectClass *klass, const void *data) |
| 439 | { |
| 440 | |
| 441 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 442 | |
| 443 | dc->realize = stm32l4x5_soc_realize; |
| 444 | /* Reason: Mapped at fixed location on the system bus */ |
| 445 | dc->user_creatable = false; |
| 446 | /* No vmstate or reset required: device has no internal state */ |
| 447 | } |
| 448 | |
| 449 | static void stm32l4x5xc_soc_class_init(ObjectClass *oc, const void *data) |
| 450 | { |
| 451 | Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); |
| 452 | |
| 453 | ssc->flash_size = 256 * KiB; |
| 454 | } |
| 455 | |
| 456 | static void stm32l4x5xe_soc_class_init(ObjectClass *oc, const void *data) |
| 457 | { |
| 458 | Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); |
| 459 | |
| 460 | ssc->flash_size = 512 * KiB; |
| 461 | } |
| 462 | |
| 463 | static void stm32l4x5xg_soc_class_init(ObjectClass *oc, const void *data) |
| 464 | { |
| 465 | Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); |
| 466 | |
| 467 | ssc->flash_size = 1 * MiB; |
| 468 | } |
| 469 | |
| 470 | static const TypeInfo stm32l4x5_soc_types[] = { |
| 471 | { |
| 472 | .name = TYPE_STM32L4X5XC_SOC, |
| 473 | .parent = TYPE_STM32L4X5_SOC, |
| 474 | .class_init = stm32l4x5xc_soc_class_init, |
| 475 | }, { |
| 476 | .name = TYPE_STM32L4X5XE_SOC, |
| 477 | .parent = TYPE_STM32L4X5_SOC, |
| 478 | .class_init = stm32l4x5xe_soc_class_init, |
| 479 | }, { |
| 480 | .name = TYPE_STM32L4X5XG_SOC, |
| 481 | .parent = TYPE_STM32L4X5_SOC, |
| 482 | .class_init = stm32l4x5xg_soc_class_init, |
| 483 | }, { |
| 484 | .name = TYPE_STM32L4X5_SOC, |
| 485 | .parent = TYPE_SYS_BUS_DEVICE, |
| 486 | .instance_size = sizeof(Stm32l4x5SocState), |
| 487 | .instance_init = stm32l4x5_soc_initfn, |
| 488 | .class_size = sizeof(Stm32l4x5SocClass), |
| 489 | .class_init = stm32l4x5_soc_class_init, |
| 490 | .abstract = true, |
| 491 | } |
| 492 | }; |
| 493 | |
| 494 | DEFINE_TYPES(stm32l4x5_soc_types) |