2 * STM32L4x5 EXTI (Extended interrupts and events controller)
4 * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
5 * Copyright (c) 2023 Samuel Tardieu <samuel.tardieu@telecom-paris.fr>
6 * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr>
8 * SPDX-License-Identifier: GPL-2.0-or-later
10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
11 * See the COPYING file in the top-level directory.
13 * This work is based on the stm32f4xx_exti by Alistair Francis.
14 * Original code is licensed under the MIT License:
16 * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me>
20 * The reference used is the STMicroElectronics RM0351 Reference manual
21 * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs.
22 * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html
25 #include "qemu/osdep.h"
29 #include "migration/vmstate.h"
30 #include "hw/misc/stm32l4x5_exti.h"
32 #define EXTI_IMR1 0x00
33 #define EXTI_EMR1 0x04
34 #define EXTI_RTSR1 0x08
35 #define EXTI_FTSR1 0x0C
36 #define EXTI_SWIER1 0x10
38 #define EXTI_IMR2 0x20
39 #define EXTI_EMR2 0x24
40 #define EXTI_RTSR2 0x28
41 #define EXTI_FTSR2 0x2C
42 #define EXTI_SWIER2 0x30
45 #define EXTI_NUM_GPIO_EVENT_IN_LINES 16
46 #define EXTI_MAX_IRQ_PER_BANK 32
47 #define EXTI_IRQS_BANK0 32
48 #define EXTI_IRQS_BANK1 8
50 static const unsigned irqs_per_bank
[EXTI_NUM_REGISTER
] = {
55 static const uint32_t exti_romask
[EXTI_NUM_REGISTER
] = {
56 0xff820000, /* 0b11111111_10000010_00000000_00000000 */
57 0x00000087, /* 0b00000000_00000000_00000000_10000111 */
60 static unsigned regbank_index_by_irq(unsigned irq
)
62 return irq
>= EXTI_MAX_IRQ_PER_BANK
? 1 : 0;
65 static unsigned regbank_index_by_addr(hwaddr addr
)
67 return addr
>= EXTI_IMR2
? 1 : 0;
70 static unsigned valid_mask(unsigned bank
)
72 return MAKE_64BIT_MASK(0, irqs_per_bank
[bank
]);
75 static unsigned configurable_mask(unsigned bank
)
77 return valid_mask(bank
) & ~exti_romask
[bank
];
80 static void stm32l4x5_exti_reset_hold(Object
*obj
)
82 Stm32l4x5ExtiState
*s
= STM32L4X5_EXTI(obj
);
84 for (unsigned bank
= 0; bank
< EXTI_NUM_REGISTER
; bank
++) {
85 s
->imr
[bank
] = exti_romask
[bank
];
86 s
->emr
[bank
] = 0x00000000;
87 s
->rtsr
[bank
] = 0x00000000;
88 s
->ftsr
[bank
] = 0x00000000;
89 s
->swier
[bank
] = 0x00000000;
90 s
->pr
[bank
] = 0x00000000;
94 static void stm32l4x5_exti_set_irq(void *opaque
, int irq
, int level
)
96 Stm32l4x5ExtiState
*s
= opaque
;
97 const unsigned bank
= regbank_index_by_irq(irq
);
100 trace_stm32l4x5_exti_set_irq(irq
, level
);
102 /* Shift the value to enable access in x2 registers. */
103 irq
%= EXTI_MAX_IRQ_PER_BANK
;
105 /* If the interrupt is masked, pr won't be raised */
106 if (!extract32(s
->imr
[bank
], irq
, 1)) {
110 if (((1 << irq
) & s
->rtsr
[bank
]) && level
) {
112 s
->pr
[bank
] |= 1 << irq
;
113 qemu_irq_pulse(s
->irq
[oirq
]);
114 } else if (((1 << irq
) & s
->ftsr
[bank
]) && !level
) {
116 s
->pr
[bank
] |= 1 << irq
;
117 qemu_irq_pulse(s
->irq
[oirq
]);
120 * In the following situations :
121 * - falling edge but rising trigger selected
122 * - rising edge but falling trigger selected
123 * - no trigger selected
124 * No action is required
128 static uint64_t stm32l4x5_exti_read(void *opaque
, hwaddr addr
,
131 Stm32l4x5ExtiState
*s
= opaque
;
133 const unsigned bank
= regbank_index_by_addr(addr
);
162 qemu_log_mask(LOG_GUEST_ERROR
,
163 "STM32L4X5_exti_read: Bad offset 0x%" HWADDR_PRIx
"\n",
168 trace_stm32l4x5_exti_read(addr
, r
);
173 static void stm32l4x5_exti_write(void *opaque
, hwaddr addr
,
174 uint64_t val64
, unsigned int size
)
176 Stm32l4x5ExtiState
*s
= opaque
;
177 const unsigned bank
= regbank_index_by_addr(addr
);
179 trace_stm32l4x5_exti_write(addr
, val64
);
184 s
->imr
[bank
] = val64
& valid_mask(bank
);
188 s
->emr
[bank
] = val64
& valid_mask(bank
);
192 s
->rtsr
[bank
] = val64
& configurable_mask(bank
);
196 s
->ftsr
[bank
] = val64
& configurable_mask(bank
);
200 const uint32_t set
= val64
& configurable_mask(bank
);
201 const uint32_t pend
= set
& ~s
->swier
[bank
] & s
->imr
[bank
] &
203 s
->swier
[bank
] = set
;
205 for (unsigned i
= 0; i
< irqs_per_bank
[bank
]; i
++) {
206 if (extract32(pend
, i
, 1)) {
207 qemu_irq_pulse(s
->irq
[i
+ 32 * bank
]);
214 const uint32_t cleared
= s
->pr
[bank
] & val64
& configurable_mask(bank
);
215 /* This bit is cleared by writing a 1 to it */
216 s
->pr
[bank
] &= ~cleared
;
217 /* Software triggered interrupts are cleared as well */
218 s
->swier
[bank
] &= ~cleared
;
222 qemu_log_mask(LOG_GUEST_ERROR
,
223 "STM32L4X5_exti_write: Bad offset 0x%" HWADDR_PRIx
"\n",
228 static const MemoryRegionOps stm32l4x5_exti_ops
= {
229 .read
= stm32l4x5_exti_read
,
230 .write
= stm32l4x5_exti_write
,
231 .endianness
= DEVICE_NATIVE_ENDIAN
,
232 .impl
.min_access_size
= 4,
233 .impl
.max_access_size
= 4,
234 .impl
.unaligned
= false,
235 .valid
.min_access_size
= 4,
236 .valid
.max_access_size
= 4,
237 .valid
.unaligned
= false,
240 static void stm32l4x5_exti_init(Object
*obj
)
242 Stm32l4x5ExtiState
*s
= STM32L4X5_EXTI(obj
);
244 for (size_t i
= 0; i
< EXTI_NUM_INTERRUPT_OUT_LINES
; i
++) {
245 sysbus_init_irq(SYS_BUS_DEVICE(obj
), &s
->irq
[i
]);
248 memory_region_init_io(&s
->mmio
, obj
, &stm32l4x5_exti_ops
, s
,
249 TYPE_STM32L4X5_EXTI
, 0x400);
250 sysbus_init_mmio(SYS_BUS_DEVICE(obj
), &s
->mmio
);
252 qdev_init_gpio_in(DEVICE(obj
), stm32l4x5_exti_set_irq
,
253 EXTI_NUM_GPIO_EVENT_IN_LINES
);
256 static const VMStateDescription vmstate_stm32l4x5_exti
= {
257 .name
= TYPE_STM32L4X5_EXTI
,
259 .minimum_version_id
= 1,
260 .fields
= (VMStateField
[]) {
261 VMSTATE_UINT32_ARRAY(imr
, Stm32l4x5ExtiState
, EXTI_NUM_REGISTER
),
262 VMSTATE_UINT32_ARRAY(emr
, Stm32l4x5ExtiState
, EXTI_NUM_REGISTER
),
263 VMSTATE_UINT32_ARRAY(rtsr
, Stm32l4x5ExtiState
, EXTI_NUM_REGISTER
),
264 VMSTATE_UINT32_ARRAY(ftsr
, Stm32l4x5ExtiState
, EXTI_NUM_REGISTER
),
265 VMSTATE_UINT32_ARRAY(swier
, Stm32l4x5ExtiState
, EXTI_NUM_REGISTER
),
266 VMSTATE_UINT32_ARRAY(pr
, Stm32l4x5ExtiState
, EXTI_NUM_REGISTER
),
267 VMSTATE_END_OF_LIST()
271 static void stm32l4x5_exti_class_init(ObjectClass
*klass
, void *data
)
273 DeviceClass
*dc
= DEVICE_CLASS(klass
);
274 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
276 dc
->vmsd
= &vmstate_stm32l4x5_exti
;
277 rc
->phases
.hold
= stm32l4x5_exti_reset_hold
;
280 static const TypeInfo stm32l4x5_exti_types
[] = {
282 .name
= TYPE_STM32L4X5_EXTI
,
283 .parent
= TYPE_SYS_BUS_DEVICE
,
284 .instance_size
= sizeof(Stm32l4x5ExtiState
),
285 .instance_init
= stm32l4x5_exti_init
,
286 .class_init
= stm32l4x5_exti_class_init
,
290 DEFINE_TYPES(stm32l4x5_exti_types
)