| 1 | /* |
| 2 | * STM32L4x5 GPIO (General Purpose Input/Ouput) |
| 3 | * |
| 4 | * Copyright (c) 2024 Arnaud Minier <arnaud.minier@telecom-paris.fr> |
| 5 | * Copyright (c) 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 | |
| 13 | /* |
| 14 | * The reference used is the STMicroElectronics RM0351 Reference manual |
| 15 | * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. |
| 16 | * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html |
| 17 | */ |
| 18 | |
| 19 | #include "qemu/osdep.h" |
| 20 | #include "qemu/log.h" |
| 21 | #include "hw/gpio/stm32l4x5_gpio.h" |
| 22 | #include "hw/core/irq.h" |
| 23 | #include "hw/core/clock.h" |
| 24 | #include "hw/core/qdev-clock.h" |
| 25 | #include "hw/core/qdev-properties.h" |
| 26 | #include "qapi/visitor.h" |
| 27 | #include "qapi/error.h" |
| 28 | #include "migration/vmstate.h" |
| 29 | #include "trace.h" |
| 30 | |
| 31 | #define GPIO_MODER 0x00 |
| 32 | #define GPIO_OTYPER 0x04 |
| 33 | #define GPIO_OSPEEDR 0x08 |
| 34 | #define GPIO_PUPDR 0x0C |
| 35 | #define GPIO_IDR 0x10 |
| 36 | #define GPIO_ODR 0x14 |
| 37 | #define GPIO_BSRR 0x18 |
| 38 | #define GPIO_LCKR 0x1C |
| 39 | #define GPIO_AFRL 0x20 |
| 40 | #define GPIO_AFRH 0x24 |
| 41 | #define GPIO_BRR 0x28 |
| 42 | #define GPIO_ASCR 0x2C |
| 43 | |
| 44 | /* 0b11111111_11111111_00000000_00000000 */ |
| 45 | #define RESERVED_BITS_MASK 0xFFFF0000 |
| 46 | |
| 47 | static void update_gpio_idr(Stm32l4x5GpioState *s); |
| 48 | |
| 49 | static bool is_pull_up(Stm32l4x5GpioState *s, unsigned pin) |
| 50 | { |
| 51 | return extract32(s->pupdr, 2 * pin, 2) == 1; |
| 52 | } |
| 53 | |
| 54 | static bool is_pull_down(Stm32l4x5GpioState *s, unsigned pin) |
| 55 | { |
| 56 | return extract32(s->pupdr, 2 * pin, 2) == 2; |
| 57 | } |
| 58 | |
| 59 | static bool is_output(Stm32l4x5GpioState *s, unsigned pin) |
| 60 | { |
| 61 | return extract32(s->moder, 2 * pin, 2) == 1; |
| 62 | } |
| 63 | |
| 64 | static bool is_open_drain(Stm32l4x5GpioState *s, unsigned pin) |
| 65 | { |
| 66 | return extract32(s->otyper, pin, 1) == 1; |
| 67 | } |
| 68 | |
| 69 | static bool is_push_pull(Stm32l4x5GpioState *s, unsigned pin) |
| 70 | { |
| 71 | return extract32(s->otyper, pin, 1) == 0; |
| 72 | } |
| 73 | |
| 74 | static void stm32l4x5_gpio_reset_hold(Object *obj, ResetType type) |
| 75 | { |
| 76 | Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj); |
| 77 | |
| 78 | s->moder = s->moder_reset; |
| 79 | s->otyper = 0x00000000; |
| 80 | s->ospeedr = s->ospeedr_reset; |
| 81 | s->pupdr = s->pupdr_reset; |
| 82 | s->idr = 0x00000000; |
| 83 | s->odr = 0x00000000; |
| 84 | s->lckr = 0x00000000; |
| 85 | s->afrl = 0x00000000; |
| 86 | s->afrh = 0x00000000; |
| 87 | s->ascr = 0x00000000; |
| 88 | |
| 89 | s->disconnected_pins = 0xFFFF; |
| 90 | s->pins_connected_high = 0x0000; |
| 91 | update_gpio_idr(s); |
| 92 | } |
| 93 | |
| 94 | static void stm32l4x5_gpio_set(void *opaque, int line, int level) |
| 95 | { |
| 96 | Stm32l4x5GpioState *s = opaque; |
| 97 | /* |
| 98 | * The pin isn't set if line is configured in output mode |
| 99 | * except if level is 0 and the output is open-drain. |
| 100 | * This way there will be no short-circuit prone situations. |
| 101 | */ |
| 102 | if (is_output(s, line) && !(is_open_drain(s, line) && (level == 0))) { |
| 103 | qemu_log_mask(LOG_GUEST_ERROR, "Line %d can't be driven externally\n", |
| 104 | line); |
| 105 | return; |
| 106 | } |
| 107 | |
| 108 | s->disconnected_pins &= ~(1 << line); |
| 109 | if (level) { |
| 110 | s->pins_connected_high |= (1 << line); |
| 111 | } else { |
| 112 | s->pins_connected_high &= ~(1 << line); |
| 113 | } |
| 114 | trace_stm32l4x5_gpio_pins(s->name, s->disconnected_pins, |
| 115 | s->pins_connected_high); |
| 116 | update_gpio_idr(s); |
| 117 | } |
| 118 | |
| 119 | |
| 120 | static void update_gpio_idr(Stm32l4x5GpioState *s) |
| 121 | { |
| 122 | uint32_t new_idr_mask = 0; |
| 123 | uint32_t new_idr = s->odr; |
| 124 | uint32_t old_idr = s->idr; |
| 125 | int new_pin_state, old_pin_state; |
| 126 | |
| 127 | for (int i = 0; i < GPIO_NUM_PINS; i++) { |
| 128 | if (is_output(s, i)) { |
| 129 | if (is_push_pull(s, i)) { |
| 130 | new_idr_mask |= (1 << i); |
| 131 | } else if (!(s->odr & (1 << i))) { |
| 132 | /* open-drain ODR 0 */ |
| 133 | new_idr_mask |= (1 << i); |
| 134 | /* open-drain ODR 1 */ |
| 135 | } else if (!(s->disconnected_pins & (1 << i)) && |
| 136 | !(s->pins_connected_high & (1 << i))) { |
| 137 | /* open-drain ODR 1 with pin connected low */ |
| 138 | new_idr_mask |= (1 << i); |
| 139 | new_idr &= ~(1 << i); |
| 140 | /* open-drain ODR 1 with unactive pin */ |
| 141 | } else if (is_pull_up(s, i)) { |
| 142 | new_idr_mask |= (1 << i); |
| 143 | } else if (is_pull_down(s, i)) { |
| 144 | new_idr_mask |= (1 << i); |
| 145 | new_idr &= ~(1 << i); |
| 146 | } |
| 147 | /* |
| 148 | * The only case left is for open-drain ODR 1 |
| 149 | * with unactive pin without pull-up or pull-down : |
| 150 | * the value is floating. |
| 151 | */ |
| 152 | /* input or analog mode with connected pin */ |
| 153 | } else if (!(s->disconnected_pins & (1 << i))) { |
| 154 | if (s->pins_connected_high & (1 << i)) { |
| 155 | /* pin high */ |
| 156 | new_idr_mask |= (1 << i); |
| 157 | new_idr |= (1 << i); |
| 158 | } else { |
| 159 | /* pin low */ |
| 160 | new_idr_mask |= (1 << i); |
| 161 | new_idr &= ~(1 << i); |
| 162 | } |
| 163 | /* input or analog mode with disconnected pin */ |
| 164 | } else { |
| 165 | if (is_pull_up(s, i)) { |
| 166 | /* pull-up */ |
| 167 | new_idr_mask |= (1 << i); |
| 168 | new_idr |= (1 << i); |
| 169 | } else if (is_pull_down(s, i)) { |
| 170 | /* pull-down */ |
| 171 | new_idr_mask |= (1 << i); |
| 172 | new_idr &= ~(1 << i); |
| 173 | } |
| 174 | /* |
| 175 | * The only case left is for a disconnected pin |
| 176 | * without pull-up or pull-down : |
| 177 | * the value is floating. |
| 178 | */ |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | s->idr = (old_idr & ~new_idr_mask) | (new_idr & new_idr_mask); |
| 183 | trace_stm32l4x5_gpio_update_idr(s->name, old_idr, s->idr); |
| 184 | |
| 185 | for (int i = 0; i < GPIO_NUM_PINS; i++) { |
| 186 | if (new_idr_mask & (1 << i)) { |
| 187 | new_pin_state = (new_idr & (1 << i)) > 0; |
| 188 | old_pin_state = (old_idr & (1 << i)) > 0; |
| 189 | if (new_pin_state > old_pin_state) { |
| 190 | qemu_irq_raise(s->pin[i]); |
| 191 | } else if (new_pin_state < old_pin_state) { |
| 192 | qemu_irq_lower(s->pin[i]); |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | /* |
| 199 | * Return mask of pins that are both configured in output |
| 200 | * mode and externally driven (except pins in open-drain |
| 201 | * mode externally set to 0). |
| 202 | */ |
| 203 | static uint32_t get_gpio_pinmask_to_disconnect(Stm32l4x5GpioState *s) |
| 204 | { |
| 205 | uint32_t pins_to_disconnect = 0; |
| 206 | for (int i = 0; i < GPIO_NUM_PINS; i++) { |
| 207 | /* for each connected pin in output mode */ |
| 208 | if (!(s->disconnected_pins & (1 << i)) && is_output(s, i)) { |
| 209 | /* if either push-pull or high level */ |
| 210 | if (is_push_pull(s, i) || s->pins_connected_high & (1 << i)) { |
| 211 | pins_to_disconnect |= (1 << i); |
| 212 | qemu_log_mask(LOG_GUEST_ERROR, |
| 213 | "Line %d can't be driven externally\n", |
| 214 | i); |
| 215 | } |
| 216 | } |
| 217 | } |
| 218 | return pins_to_disconnect; |
| 219 | } |
| 220 | |
| 221 | /* |
| 222 | * Set field `disconnected_pins` and call `update_gpio_idr()` |
| 223 | */ |
| 224 | static void disconnect_gpio_pins(Stm32l4x5GpioState *s, uint16_t lines) |
| 225 | { |
| 226 | s->disconnected_pins |= lines; |
| 227 | trace_stm32l4x5_gpio_pins(s->name, s->disconnected_pins, |
| 228 | s->pins_connected_high); |
| 229 | update_gpio_idr(s); |
| 230 | } |
| 231 | |
| 232 | static void disconnected_pins_set(Object *obj, Visitor *v, |
| 233 | const char *name, void *opaque, Error **errp) |
| 234 | { |
| 235 | Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj); |
| 236 | uint16_t value; |
| 237 | if (!visit_type_uint16(v, name, &value, errp)) { |
| 238 | return; |
| 239 | } |
| 240 | disconnect_gpio_pins(s, value); |
| 241 | } |
| 242 | |
| 243 | static void disconnected_pins_get(Object *obj, Visitor *v, |
| 244 | const char *name, void *opaque, Error **errp) |
| 245 | { |
| 246 | visit_type_uint16(v, name, (uint16_t *)opaque, errp); |
| 247 | } |
| 248 | |
| 249 | static void clock_freq_get(Object *obj, Visitor *v, |
| 250 | const char *name, void *opaque, Error **errp) |
| 251 | { |
| 252 | Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj); |
| 253 | uint32_t clock_freq_hz = clock_get_hz(s->clk); |
| 254 | visit_type_uint32(v, name, &clock_freq_hz, errp); |
| 255 | } |
| 256 | |
| 257 | static void stm32l4x5_gpio_write(void *opaque, hwaddr addr, |
| 258 | uint64_t val64, unsigned int size) |
| 259 | { |
| 260 | Stm32l4x5GpioState *s = opaque; |
| 261 | |
| 262 | uint32_t value = val64; |
| 263 | trace_stm32l4x5_gpio_write(s->name, addr, val64); |
| 264 | |
| 265 | switch (addr) { |
| 266 | case GPIO_MODER: |
| 267 | s->moder = value; |
| 268 | disconnect_gpio_pins(s, get_gpio_pinmask_to_disconnect(s)); |
| 269 | qemu_log_mask(LOG_UNIMP, |
| 270 | "%s: Analog and AF modes aren't supported\n\ |
| 271 | Analog and AF mode behave like input mode\n", |
| 272 | __func__); |
| 273 | return; |
| 274 | case GPIO_OTYPER: |
| 275 | s->otyper = value & ~RESERVED_BITS_MASK; |
| 276 | disconnect_gpio_pins(s, get_gpio_pinmask_to_disconnect(s)); |
| 277 | return; |
| 278 | case GPIO_OSPEEDR: |
| 279 | qemu_log_mask(LOG_UNIMP, |
| 280 | "%s: Changing I/O output speed isn't supported\n\ |
| 281 | I/O speed is already maximal\n", |
| 282 | __func__); |
| 283 | s->ospeedr = value; |
| 284 | return; |
| 285 | case GPIO_PUPDR: |
| 286 | s->pupdr = value; |
| 287 | update_gpio_idr(s); |
| 288 | return; |
| 289 | case GPIO_IDR: |
| 290 | qemu_log_mask(LOG_UNIMP, |
| 291 | "%s: GPIO->IDR is read-only\n", |
| 292 | __func__); |
| 293 | return; |
| 294 | case GPIO_ODR: |
| 295 | s->odr = value & ~RESERVED_BITS_MASK; |
| 296 | update_gpio_idr(s); |
| 297 | return; |
| 298 | case GPIO_BSRR: { |
| 299 | uint32_t bits_to_reset = (value & RESERVED_BITS_MASK) >> GPIO_NUM_PINS; |
| 300 | uint32_t bits_to_set = value & ~RESERVED_BITS_MASK; |
| 301 | /* If both BSx and BRx are set, BSx has priority.*/ |
| 302 | s->odr &= ~bits_to_reset; |
| 303 | s->odr |= bits_to_set; |
| 304 | update_gpio_idr(s); |
| 305 | return; |
| 306 | } |
| 307 | case GPIO_LCKR: |
| 308 | qemu_log_mask(LOG_UNIMP, |
| 309 | "%s: Locking port bits configuration isn't supported\n", |
| 310 | __func__); |
| 311 | s->lckr = value & ~RESERVED_BITS_MASK; |
| 312 | return; |
| 313 | case GPIO_AFRL: |
| 314 | qemu_log_mask(LOG_UNIMP, |
| 315 | "%s: Alternate functions aren't supported\n", |
| 316 | __func__); |
| 317 | s->afrl = value; |
| 318 | return; |
| 319 | case GPIO_AFRH: |
| 320 | qemu_log_mask(LOG_UNIMP, |
| 321 | "%s: Alternate functions aren't supported\n", |
| 322 | __func__); |
| 323 | s->afrh = value; |
| 324 | return; |
| 325 | case GPIO_BRR: { |
| 326 | uint32_t bits_to_reset = value & ~RESERVED_BITS_MASK; |
| 327 | s->odr &= ~bits_to_reset; |
| 328 | update_gpio_idr(s); |
| 329 | return; |
| 330 | } |
| 331 | case GPIO_ASCR: |
| 332 | qemu_log_mask(LOG_UNIMP, |
| 333 | "%s: ADC function isn't supported\n", |
| 334 | __func__); |
| 335 | s->ascr = value & ~RESERVED_BITS_MASK; |
| 336 | return; |
| 337 | default: |
| 338 | qemu_log_mask(LOG_GUEST_ERROR, |
| 339 | "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | static uint64_t stm32l4x5_gpio_read(void *opaque, hwaddr addr, |
| 344 | unsigned int size) |
| 345 | { |
| 346 | Stm32l4x5GpioState *s = opaque; |
| 347 | |
| 348 | trace_stm32l4x5_gpio_read(s->name, addr); |
| 349 | |
| 350 | switch (addr) { |
| 351 | case GPIO_MODER: |
| 352 | return s->moder; |
| 353 | case GPIO_OTYPER: |
| 354 | return s->otyper; |
| 355 | case GPIO_OSPEEDR: |
| 356 | return s->ospeedr; |
| 357 | case GPIO_PUPDR: |
| 358 | return s->pupdr; |
| 359 | case GPIO_IDR: |
| 360 | return s->idr; |
| 361 | case GPIO_ODR: |
| 362 | return s->odr; |
| 363 | case GPIO_BSRR: |
| 364 | return 0; |
| 365 | case GPIO_LCKR: |
| 366 | return s->lckr; |
| 367 | case GPIO_AFRL: |
| 368 | return s->afrl; |
| 369 | case GPIO_AFRH: |
| 370 | return s->afrh; |
| 371 | case GPIO_BRR: |
| 372 | return 0; |
| 373 | case GPIO_ASCR: |
| 374 | return s->ascr; |
| 375 | default: |
| 376 | qemu_log_mask(LOG_GUEST_ERROR, |
| 377 | "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); |
| 378 | return 0; |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | static const MemoryRegionOps stm32l4x5_gpio_ops = { |
| 383 | .read = stm32l4x5_gpio_read, |
| 384 | .write = stm32l4x5_gpio_write, |
| 385 | .endianness = DEVICE_NATIVE_ENDIAN, |
| 386 | .impl = { |
| 387 | .min_access_size = 4, |
| 388 | .max_access_size = 4, |
| 389 | .unaligned = false, |
| 390 | }, |
| 391 | .valid = { |
| 392 | .min_access_size = 4, |
| 393 | .max_access_size = 4, |
| 394 | .unaligned = false, |
| 395 | }, |
| 396 | }; |
| 397 | |
| 398 | static void stm32l4x5_gpio_init(Object *obj) |
| 399 | { |
| 400 | Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj); |
| 401 | |
| 402 | memory_region_init_io(&s->mmio, obj, &stm32l4x5_gpio_ops, s, |
| 403 | TYPE_STM32L4X5_GPIO, 0x400); |
| 404 | |
| 405 | sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); |
| 406 | |
| 407 | qdev_init_gpio_out(DEVICE(obj), s->pin, GPIO_NUM_PINS); |
| 408 | qdev_init_gpio_in(DEVICE(obj), stm32l4x5_gpio_set, GPIO_NUM_PINS); |
| 409 | |
| 410 | s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0); |
| 411 | |
| 412 | object_property_add(obj, "disconnected-pins", "uint16", |
| 413 | disconnected_pins_get, disconnected_pins_set, |
| 414 | NULL, &s->disconnected_pins); |
| 415 | object_property_add(obj, "clock-freq-hz", "uint32", |
| 416 | clock_freq_get, NULL, NULL, NULL); |
| 417 | } |
| 418 | |
| 419 | static void stm32l4x5_gpio_realize(DeviceState *dev, Error **errp) |
| 420 | { |
| 421 | Stm32l4x5GpioState *s = STM32L4X5_GPIO(dev); |
| 422 | if (!clock_has_source(s->clk)) { |
| 423 | error_setg(errp, "GPIO: clk input must be connected"); |
| 424 | return; |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | static const VMStateDescription vmstate_stm32l4x5_gpio = { |
| 429 | .name = TYPE_STM32L4X5_GPIO, |
| 430 | .version_id = 2, |
| 431 | .minimum_version_id = 2, |
| 432 | .fields = (VMStateField[]){ |
| 433 | VMSTATE_UINT32(moder, Stm32l4x5GpioState), |
| 434 | VMSTATE_UINT32(otyper, Stm32l4x5GpioState), |
| 435 | VMSTATE_UINT32(ospeedr, Stm32l4x5GpioState), |
| 436 | VMSTATE_UINT32(pupdr, Stm32l4x5GpioState), |
| 437 | VMSTATE_UINT32(idr, Stm32l4x5GpioState), |
| 438 | VMSTATE_UINT32(odr, Stm32l4x5GpioState), |
| 439 | VMSTATE_UINT32(lckr, Stm32l4x5GpioState), |
| 440 | VMSTATE_UINT32(afrl, Stm32l4x5GpioState), |
| 441 | VMSTATE_UINT32(afrh, Stm32l4x5GpioState), |
| 442 | VMSTATE_UINT32(ascr, Stm32l4x5GpioState), |
| 443 | VMSTATE_UINT16(disconnected_pins, Stm32l4x5GpioState), |
| 444 | VMSTATE_UINT16(pins_connected_high, Stm32l4x5GpioState), |
| 445 | VMSTATE_CLOCK(clk, Stm32l4x5GpioState), |
| 446 | VMSTATE_END_OF_LIST() |
| 447 | } |
| 448 | }; |
| 449 | |
| 450 | static const Property stm32l4x5_gpio_properties[] = { |
| 451 | DEFINE_PROP_STRING("name", Stm32l4x5GpioState, name), |
| 452 | DEFINE_PROP_UINT32("mode-reset", Stm32l4x5GpioState, moder_reset, 0), |
| 453 | DEFINE_PROP_UINT32("ospeed-reset", Stm32l4x5GpioState, ospeedr_reset, 0), |
| 454 | DEFINE_PROP_UINT32("pupd-reset", Stm32l4x5GpioState, pupdr_reset, 0), |
| 455 | }; |
| 456 | |
| 457 | static void stm32l4x5_gpio_class_init(ObjectClass *klass, const void *data) |
| 458 | { |
| 459 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 460 | ResettableClass *rc = RESETTABLE_CLASS(klass); |
| 461 | |
| 462 | device_class_set_props(dc, stm32l4x5_gpio_properties); |
| 463 | dc->vmsd = &vmstate_stm32l4x5_gpio; |
| 464 | dc->realize = stm32l4x5_gpio_realize; |
| 465 | rc->phases.hold = stm32l4x5_gpio_reset_hold; |
| 466 | } |
| 467 | |
| 468 | static const TypeInfo stm32l4x5_gpio_types[] = { |
| 469 | { |
| 470 | .name = TYPE_STM32L4X5_GPIO, |
| 471 | .parent = TYPE_SYS_BUS_DEVICE, |
| 472 | .instance_size = sizeof(Stm32l4x5GpioState), |
| 473 | .instance_init = stm32l4x5_gpio_init, |
| 474 | .class_init = stm32l4x5_gpio_class_init, |
| 475 | }, |
| 476 | }; |
| 477 | |
| 478 | DEFINE_TYPES(stm32l4x5_gpio_types) |