xref: /openbmc/qemu/hw/gpio/stm32l4x5_gpio.c (revision ad80e367)
11cdcfb6eSInès Varhol /*
21cdcfb6eSInès Varhol  * STM32L4x5 GPIO (General Purpose Input/Ouput)
31cdcfb6eSInès Varhol  *
41cdcfb6eSInès Varhol  * Copyright (c) 2024 Arnaud Minier <arnaud.minier@telecom-paris.fr>
51cdcfb6eSInès Varhol  * Copyright (c) 2024 Inès Varhol <ines.varhol@telecom-paris.fr>
61cdcfb6eSInès Varhol  *
71cdcfb6eSInès Varhol  * SPDX-License-Identifier: GPL-2.0-or-later
81cdcfb6eSInès Varhol  *
91cdcfb6eSInès Varhol  * This work is licensed under the terms of the GNU GPL, version 2 or later.
101cdcfb6eSInès Varhol  * See the COPYING file in the top-level directory.
111cdcfb6eSInès Varhol  */
121cdcfb6eSInès Varhol 
131cdcfb6eSInès Varhol /*
141cdcfb6eSInès Varhol  * The reference used is the STMicroElectronics RM0351 Reference manual
151cdcfb6eSInès Varhol  * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs.
161cdcfb6eSInès Varhol  * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html
171cdcfb6eSInès Varhol  */
181cdcfb6eSInès Varhol 
191cdcfb6eSInès Varhol #include "qemu/osdep.h"
201cdcfb6eSInès Varhol #include "qemu/log.h"
211cdcfb6eSInès Varhol #include "hw/gpio/stm32l4x5_gpio.h"
221cdcfb6eSInès Varhol #include "hw/irq.h"
231cdcfb6eSInès Varhol #include "hw/qdev-clock.h"
241cdcfb6eSInès Varhol #include "hw/qdev-properties.h"
251cdcfb6eSInès Varhol #include "qapi/visitor.h"
261cdcfb6eSInès Varhol #include "qapi/error.h"
271cdcfb6eSInès Varhol #include "migration/vmstate.h"
281cdcfb6eSInès Varhol #include "trace.h"
291cdcfb6eSInès Varhol 
301cdcfb6eSInès Varhol #define GPIO_MODER 0x00
311cdcfb6eSInès Varhol #define GPIO_OTYPER 0x04
321cdcfb6eSInès Varhol #define GPIO_OSPEEDR 0x08
331cdcfb6eSInès Varhol #define GPIO_PUPDR 0x0C
341cdcfb6eSInès Varhol #define GPIO_IDR 0x10
351cdcfb6eSInès Varhol #define GPIO_ODR 0x14
361cdcfb6eSInès Varhol #define GPIO_BSRR 0x18
371cdcfb6eSInès Varhol #define GPIO_LCKR 0x1C
381cdcfb6eSInès Varhol #define GPIO_AFRL 0x20
391cdcfb6eSInès Varhol #define GPIO_AFRH 0x24
401cdcfb6eSInès Varhol #define GPIO_BRR 0x28
411cdcfb6eSInès Varhol #define GPIO_ASCR 0x2C
421cdcfb6eSInès Varhol 
431cdcfb6eSInès Varhol /* 0b11111111_11111111_00000000_00000000 */
441cdcfb6eSInès Varhol #define RESERVED_BITS_MASK 0xFFFF0000
451cdcfb6eSInès Varhol 
461cdcfb6eSInès Varhol static void update_gpio_idr(Stm32l4x5GpioState *s);
471cdcfb6eSInès Varhol 
481cdcfb6eSInès Varhol static bool is_pull_up(Stm32l4x5GpioState *s, unsigned pin)
491cdcfb6eSInès Varhol {
501cdcfb6eSInès Varhol     return extract32(s->pupdr, 2 * pin, 2) == 1;
511cdcfb6eSInès Varhol }
521cdcfb6eSInès Varhol 
531cdcfb6eSInès Varhol static bool is_pull_down(Stm32l4x5GpioState *s, unsigned pin)
541cdcfb6eSInès Varhol {
551cdcfb6eSInès Varhol     return extract32(s->pupdr, 2 * pin, 2) == 2;
561cdcfb6eSInès Varhol }
571cdcfb6eSInès Varhol 
581cdcfb6eSInès Varhol static bool is_output(Stm32l4x5GpioState *s, unsigned pin)
591cdcfb6eSInès Varhol {
601cdcfb6eSInès Varhol     return extract32(s->moder, 2 * pin, 2) == 1;
611cdcfb6eSInès Varhol }
621cdcfb6eSInès Varhol 
631cdcfb6eSInès Varhol static bool is_open_drain(Stm32l4x5GpioState *s, unsigned pin)
641cdcfb6eSInès Varhol {
651cdcfb6eSInès Varhol     return extract32(s->otyper, pin, 1) == 1;
661cdcfb6eSInès Varhol }
671cdcfb6eSInès Varhol 
681cdcfb6eSInès Varhol static bool is_push_pull(Stm32l4x5GpioState *s, unsigned pin)
691cdcfb6eSInès Varhol {
701cdcfb6eSInès Varhol     return extract32(s->otyper, pin, 1) == 0;
711cdcfb6eSInès Varhol }
721cdcfb6eSInès Varhol 
73*ad80e367SPeter Maydell static void stm32l4x5_gpio_reset_hold(Object *obj, ResetType type)
741cdcfb6eSInès Varhol {
751cdcfb6eSInès Varhol     Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj);
761cdcfb6eSInès Varhol 
771cdcfb6eSInès Varhol     s->moder = s->moder_reset;
781cdcfb6eSInès Varhol     s->otyper = 0x00000000;
791cdcfb6eSInès Varhol     s->ospeedr = s->ospeedr_reset;
801cdcfb6eSInès Varhol     s->pupdr = s->pupdr_reset;
811cdcfb6eSInès Varhol     s->idr = 0x00000000;
821cdcfb6eSInès Varhol     s->odr = 0x00000000;
831cdcfb6eSInès Varhol     s->lckr = 0x00000000;
841cdcfb6eSInès Varhol     s->afrl = 0x00000000;
851cdcfb6eSInès Varhol     s->afrh = 0x00000000;
861cdcfb6eSInès Varhol     s->ascr = 0x00000000;
871cdcfb6eSInès Varhol 
881cdcfb6eSInès Varhol     s->disconnected_pins = 0xFFFF;
891cdcfb6eSInès Varhol     s->pins_connected_high = 0x0000;
901cdcfb6eSInès Varhol     update_gpio_idr(s);
911cdcfb6eSInès Varhol }
921cdcfb6eSInès Varhol 
931cdcfb6eSInès Varhol static void stm32l4x5_gpio_set(void *opaque, int line, int level)
941cdcfb6eSInès Varhol {
951cdcfb6eSInès Varhol     Stm32l4x5GpioState *s = opaque;
961cdcfb6eSInès Varhol     /*
971cdcfb6eSInès Varhol      * The pin isn't set if line is configured in output mode
981cdcfb6eSInès Varhol      * except if level is 0 and the output is open-drain.
991cdcfb6eSInès Varhol      * This way there will be no short-circuit prone situations.
1001cdcfb6eSInès Varhol      */
1011cdcfb6eSInès Varhol     if (is_output(s, line) && !(is_open_drain(s, line) && (level == 0))) {
1021cdcfb6eSInès Varhol         qemu_log_mask(LOG_GUEST_ERROR, "Line %d can't be driven externally\n",
1031cdcfb6eSInès Varhol                       line);
1041cdcfb6eSInès Varhol         return;
1051cdcfb6eSInès Varhol     }
1061cdcfb6eSInès Varhol 
1071cdcfb6eSInès Varhol     s->disconnected_pins &= ~(1 << line);
1081cdcfb6eSInès Varhol     if (level) {
1091cdcfb6eSInès Varhol         s->pins_connected_high |= (1 << line);
1101cdcfb6eSInès Varhol     } else {
1111cdcfb6eSInès Varhol         s->pins_connected_high &= ~(1 << line);
1121cdcfb6eSInès Varhol     }
1131cdcfb6eSInès Varhol     trace_stm32l4x5_gpio_pins(s->name, s->disconnected_pins,
1141cdcfb6eSInès Varhol                               s->pins_connected_high);
1151cdcfb6eSInès Varhol     update_gpio_idr(s);
1161cdcfb6eSInès Varhol }
1171cdcfb6eSInès Varhol 
1181cdcfb6eSInès Varhol 
1191cdcfb6eSInès Varhol static void update_gpio_idr(Stm32l4x5GpioState *s)
1201cdcfb6eSInès Varhol {
1211cdcfb6eSInès Varhol     uint32_t new_idr_mask = 0;
1221cdcfb6eSInès Varhol     uint32_t new_idr = s->odr;
1231cdcfb6eSInès Varhol     uint32_t old_idr = s->idr;
1241cdcfb6eSInès Varhol     int new_pin_state, old_pin_state;
1251cdcfb6eSInès Varhol 
1261cdcfb6eSInès Varhol     for (int i = 0; i < GPIO_NUM_PINS; i++) {
1271cdcfb6eSInès Varhol         if (is_output(s, i)) {
1281cdcfb6eSInès Varhol             if (is_push_pull(s, i)) {
1291cdcfb6eSInès Varhol                 new_idr_mask |= (1 << i);
1301cdcfb6eSInès Varhol             } else if (!(s->odr & (1 << i))) {
1311cdcfb6eSInès Varhol                 /* open-drain ODR 0 */
1321cdcfb6eSInès Varhol                 new_idr_mask |= (1 << i);
1331cdcfb6eSInès Varhol             /* open-drain ODR 1 */
1341cdcfb6eSInès Varhol             } else if (!(s->disconnected_pins & (1 << i)) &&
1351cdcfb6eSInès Varhol                        !(s->pins_connected_high & (1 << i))) {
1361cdcfb6eSInès Varhol                 /* open-drain ODR 1 with pin connected low */
1371cdcfb6eSInès Varhol                 new_idr_mask |= (1 << i);
1381cdcfb6eSInès Varhol                 new_idr &= ~(1 << i);
1391cdcfb6eSInès Varhol             /* open-drain ODR 1 with unactive pin */
1401cdcfb6eSInès Varhol             } else if (is_pull_up(s, i)) {
1411cdcfb6eSInès Varhol                 new_idr_mask |= (1 << i);
1421cdcfb6eSInès Varhol             } else if (is_pull_down(s, i)) {
1431cdcfb6eSInès Varhol                 new_idr_mask |= (1 << i);
1441cdcfb6eSInès Varhol                 new_idr &= ~(1 << i);
1451cdcfb6eSInès Varhol             }
1461cdcfb6eSInès Varhol             /*
1471cdcfb6eSInès Varhol              * The only case left is for open-drain ODR 1
1481cdcfb6eSInès Varhol              * with unactive pin without pull-up or pull-down :
1491cdcfb6eSInès Varhol              * the value is floating.
1501cdcfb6eSInès Varhol              */
1511cdcfb6eSInès Varhol         /* input or analog mode with connected pin */
1521cdcfb6eSInès Varhol         } else if (!(s->disconnected_pins & (1 << i))) {
1531cdcfb6eSInès Varhol             if (s->pins_connected_high & (1 << i)) {
1541cdcfb6eSInès Varhol                 /* pin high */
1551cdcfb6eSInès Varhol                 new_idr_mask |= (1 << i);
1561cdcfb6eSInès Varhol                 new_idr |= (1 << i);
1571cdcfb6eSInès Varhol             } else {
1581cdcfb6eSInès Varhol                 /* pin low */
1591cdcfb6eSInès Varhol                 new_idr_mask |= (1 << i);
1601cdcfb6eSInès Varhol                 new_idr &= ~(1 << i);
1611cdcfb6eSInès Varhol             }
1621cdcfb6eSInès Varhol         /* input or analog mode with disconnected pin */
1631cdcfb6eSInès Varhol         } else {
1641cdcfb6eSInès Varhol             if (is_pull_up(s, i)) {
1651cdcfb6eSInès Varhol                 /* pull-up */
1661cdcfb6eSInès Varhol                 new_idr_mask |= (1 << i);
1671cdcfb6eSInès Varhol                 new_idr |= (1 << i);
1681cdcfb6eSInès Varhol             } else if (is_pull_down(s, i)) {
1691cdcfb6eSInès Varhol                 /* pull-down */
1701cdcfb6eSInès Varhol                 new_idr_mask |= (1 << i);
1711cdcfb6eSInès Varhol                 new_idr &= ~(1 << i);
1721cdcfb6eSInès Varhol             }
1731cdcfb6eSInès Varhol             /*
1741cdcfb6eSInès Varhol              * The only case left is for a disconnected pin
1751cdcfb6eSInès Varhol              * without pull-up or pull-down :
1761cdcfb6eSInès Varhol              * the value is floating.
1771cdcfb6eSInès Varhol              */
1781cdcfb6eSInès Varhol         }
1791cdcfb6eSInès Varhol     }
1801cdcfb6eSInès Varhol 
1811cdcfb6eSInès Varhol     s->idr = (old_idr & ~new_idr_mask) | (new_idr & new_idr_mask);
1821cdcfb6eSInès Varhol     trace_stm32l4x5_gpio_update_idr(s->name, old_idr, s->idr);
1831cdcfb6eSInès Varhol 
1841cdcfb6eSInès Varhol     for (int i = 0; i < GPIO_NUM_PINS; i++) {
1851cdcfb6eSInès Varhol         if (new_idr_mask & (1 << i)) {
1861cdcfb6eSInès Varhol             new_pin_state = (new_idr & (1 << i)) > 0;
1871cdcfb6eSInès Varhol             old_pin_state = (old_idr & (1 << i)) > 0;
1881cdcfb6eSInès Varhol             if (new_pin_state > old_pin_state) {
1891cdcfb6eSInès Varhol                 qemu_irq_raise(s->pin[i]);
1901cdcfb6eSInès Varhol             } else if (new_pin_state < old_pin_state) {
1911cdcfb6eSInès Varhol                 qemu_irq_lower(s->pin[i]);
1921cdcfb6eSInès Varhol             }
1931cdcfb6eSInès Varhol         }
1941cdcfb6eSInès Varhol     }
1951cdcfb6eSInès Varhol }
1961cdcfb6eSInès Varhol 
1971cdcfb6eSInès Varhol /*
1981cdcfb6eSInès Varhol  * Return mask of pins that are both configured in output
1991cdcfb6eSInès Varhol  * mode and externally driven (except pins in open-drain
2001cdcfb6eSInès Varhol  * mode externally set to 0).
2011cdcfb6eSInès Varhol  */
2021cdcfb6eSInès Varhol static uint32_t get_gpio_pinmask_to_disconnect(Stm32l4x5GpioState *s)
2031cdcfb6eSInès Varhol {
2041cdcfb6eSInès Varhol     uint32_t pins_to_disconnect = 0;
2051cdcfb6eSInès Varhol     for (int i = 0; i < GPIO_NUM_PINS; i++) {
2061cdcfb6eSInès Varhol         /* for each connected pin in output mode */
2071cdcfb6eSInès Varhol         if (!(s->disconnected_pins & (1 << i)) && is_output(s, i)) {
2081cdcfb6eSInès Varhol             /* if either push-pull or high level */
2091cdcfb6eSInès Varhol             if (is_push_pull(s, i) || s->pins_connected_high & (1 << i)) {
2101cdcfb6eSInès Varhol                 pins_to_disconnect |= (1 << i);
2111cdcfb6eSInès Varhol                 qemu_log_mask(LOG_GUEST_ERROR,
2121cdcfb6eSInès Varhol                               "Line %d can't be driven externally\n",
2131cdcfb6eSInès Varhol                               i);
2141cdcfb6eSInès Varhol             }
2151cdcfb6eSInès Varhol         }
2161cdcfb6eSInès Varhol     }
2171cdcfb6eSInès Varhol     return pins_to_disconnect;
2181cdcfb6eSInès Varhol }
2191cdcfb6eSInès Varhol 
2201cdcfb6eSInès Varhol /*
2211cdcfb6eSInès Varhol  * Set field `disconnected_pins` and call `update_gpio_idr()`
2221cdcfb6eSInès Varhol  */
2231cdcfb6eSInès Varhol static void disconnect_gpio_pins(Stm32l4x5GpioState *s, uint16_t lines)
2241cdcfb6eSInès Varhol {
2251cdcfb6eSInès Varhol     s->disconnected_pins |= lines;
2261cdcfb6eSInès Varhol     trace_stm32l4x5_gpio_pins(s->name, s->disconnected_pins,
2271cdcfb6eSInès Varhol                               s->pins_connected_high);
2281cdcfb6eSInès Varhol     update_gpio_idr(s);
2291cdcfb6eSInès Varhol }
2301cdcfb6eSInès Varhol 
2311cdcfb6eSInès Varhol static void disconnected_pins_set(Object *obj, Visitor *v,
2321cdcfb6eSInès Varhol     const char *name, void *opaque, Error **errp)
2331cdcfb6eSInès Varhol {
2341cdcfb6eSInès Varhol     Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj);
2351cdcfb6eSInès Varhol     uint16_t value;
2361cdcfb6eSInès Varhol     if (!visit_type_uint16(v, name, &value, errp)) {
2371cdcfb6eSInès Varhol         return;
2381cdcfb6eSInès Varhol     }
2391cdcfb6eSInès Varhol     disconnect_gpio_pins(s, value);
2401cdcfb6eSInès Varhol }
2411cdcfb6eSInès Varhol 
2421cdcfb6eSInès Varhol static void disconnected_pins_get(Object *obj, Visitor *v,
2431cdcfb6eSInès Varhol     const char *name, void *opaque, Error **errp)
2441cdcfb6eSInès Varhol {
2451cdcfb6eSInès Varhol     visit_type_uint16(v, name, (uint16_t *)opaque, errp);
2461cdcfb6eSInès Varhol }
2471cdcfb6eSInès Varhol 
2481cdcfb6eSInès Varhol static void clock_freq_get(Object *obj, Visitor *v,
2491cdcfb6eSInès Varhol     const char *name, void *opaque, Error **errp)
2501cdcfb6eSInès Varhol {
2511cdcfb6eSInès Varhol     Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj);
2521cdcfb6eSInès Varhol     uint32_t clock_freq_hz = clock_get_hz(s->clk);
2531cdcfb6eSInès Varhol     visit_type_uint32(v, name, &clock_freq_hz, errp);
2541cdcfb6eSInès Varhol }
2551cdcfb6eSInès Varhol 
2561cdcfb6eSInès Varhol static void stm32l4x5_gpio_write(void *opaque, hwaddr addr,
2571cdcfb6eSInès Varhol                                  uint64_t val64, unsigned int size)
2581cdcfb6eSInès Varhol {
2591cdcfb6eSInès Varhol     Stm32l4x5GpioState *s = opaque;
2601cdcfb6eSInès Varhol 
2611cdcfb6eSInès Varhol     uint32_t value = val64;
2621cdcfb6eSInès Varhol     trace_stm32l4x5_gpio_write(s->name, addr, val64);
2631cdcfb6eSInès Varhol 
2641cdcfb6eSInès Varhol     switch (addr) {
2651cdcfb6eSInès Varhol     case GPIO_MODER:
2661cdcfb6eSInès Varhol         s->moder = value;
2671cdcfb6eSInès Varhol         disconnect_gpio_pins(s, get_gpio_pinmask_to_disconnect(s));
2681cdcfb6eSInès Varhol         qemu_log_mask(LOG_UNIMP,
2691cdcfb6eSInès Varhol                       "%s: Analog and AF modes aren't supported\n\
2701cdcfb6eSInès Varhol                        Analog and AF mode behave like input mode\n",
2711cdcfb6eSInès Varhol                       __func__);
2721cdcfb6eSInès Varhol         return;
2731cdcfb6eSInès Varhol     case GPIO_OTYPER:
2741cdcfb6eSInès Varhol         s->otyper = value & ~RESERVED_BITS_MASK;
2751cdcfb6eSInès Varhol         disconnect_gpio_pins(s, get_gpio_pinmask_to_disconnect(s));
2761cdcfb6eSInès Varhol         return;
2771cdcfb6eSInès Varhol     case GPIO_OSPEEDR:
2781cdcfb6eSInès Varhol         qemu_log_mask(LOG_UNIMP,
2791cdcfb6eSInès Varhol                       "%s: Changing I/O output speed isn't supported\n\
2801cdcfb6eSInès Varhol                        I/O speed is already maximal\n",
2811cdcfb6eSInès Varhol                       __func__);
2821cdcfb6eSInès Varhol         s->ospeedr = value;
2831cdcfb6eSInès Varhol         return;
2841cdcfb6eSInès Varhol     case GPIO_PUPDR:
2851cdcfb6eSInès Varhol         s->pupdr = value;
2861cdcfb6eSInès Varhol         update_gpio_idr(s);
2871cdcfb6eSInès Varhol         return;
2881cdcfb6eSInès Varhol     case GPIO_IDR:
2891cdcfb6eSInès Varhol         qemu_log_mask(LOG_UNIMP,
2901cdcfb6eSInès Varhol                       "%s: GPIO->IDR is read-only\n",
2911cdcfb6eSInès Varhol                       __func__);
2921cdcfb6eSInès Varhol         return;
2931cdcfb6eSInès Varhol     case GPIO_ODR:
2941cdcfb6eSInès Varhol         s->odr = value & ~RESERVED_BITS_MASK;
2951cdcfb6eSInès Varhol         update_gpio_idr(s);
2961cdcfb6eSInès Varhol         return;
2971cdcfb6eSInès Varhol     case GPIO_BSRR: {
2981cdcfb6eSInès Varhol         uint32_t bits_to_reset = (value & RESERVED_BITS_MASK) >> GPIO_NUM_PINS;
2991cdcfb6eSInès Varhol         uint32_t bits_to_set = value & ~RESERVED_BITS_MASK;
3001cdcfb6eSInès Varhol         /* If both BSx and BRx are set, BSx has priority.*/
3011cdcfb6eSInès Varhol         s->odr &= ~bits_to_reset;
3021cdcfb6eSInès Varhol         s->odr |= bits_to_set;
3031cdcfb6eSInès Varhol         update_gpio_idr(s);
3041cdcfb6eSInès Varhol         return;
3051cdcfb6eSInès Varhol     }
3061cdcfb6eSInès Varhol     case GPIO_LCKR:
3071cdcfb6eSInès Varhol         qemu_log_mask(LOG_UNIMP,
3081cdcfb6eSInès Varhol                       "%s: Locking port bits configuration isn't supported\n",
3091cdcfb6eSInès Varhol                       __func__);
3101cdcfb6eSInès Varhol         s->lckr = value & ~RESERVED_BITS_MASK;
3111cdcfb6eSInès Varhol         return;
3121cdcfb6eSInès Varhol     case GPIO_AFRL:
3131cdcfb6eSInès Varhol         qemu_log_mask(LOG_UNIMP,
3141cdcfb6eSInès Varhol                       "%s: Alternate functions aren't supported\n",
3151cdcfb6eSInès Varhol                       __func__);
3161cdcfb6eSInès Varhol         s->afrl = value;
3171cdcfb6eSInès Varhol         return;
3181cdcfb6eSInès Varhol     case GPIO_AFRH:
3191cdcfb6eSInès Varhol         qemu_log_mask(LOG_UNIMP,
3201cdcfb6eSInès Varhol                       "%s: Alternate functions aren't supported\n",
3211cdcfb6eSInès Varhol                       __func__);
3221cdcfb6eSInès Varhol         s->afrh = value;
3231cdcfb6eSInès Varhol         return;
3241cdcfb6eSInès Varhol     case GPIO_BRR: {
3251cdcfb6eSInès Varhol         uint32_t bits_to_reset = value & ~RESERVED_BITS_MASK;
3261cdcfb6eSInès Varhol         s->odr &= ~bits_to_reset;
3271cdcfb6eSInès Varhol         update_gpio_idr(s);
3281cdcfb6eSInès Varhol         return;
3291cdcfb6eSInès Varhol     }
3301cdcfb6eSInès Varhol     case GPIO_ASCR:
3311cdcfb6eSInès Varhol         qemu_log_mask(LOG_UNIMP,
3321cdcfb6eSInès Varhol                       "%s: ADC function isn't supported\n",
3331cdcfb6eSInès Varhol                       __func__);
3341cdcfb6eSInès Varhol         s->ascr = value & ~RESERVED_BITS_MASK;
3351cdcfb6eSInès Varhol         return;
3361cdcfb6eSInès Varhol     default:
3371cdcfb6eSInès Varhol         qemu_log_mask(LOG_GUEST_ERROR,
3381cdcfb6eSInès Varhol                       "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
3391cdcfb6eSInès Varhol     }
3401cdcfb6eSInès Varhol }
3411cdcfb6eSInès Varhol 
3421cdcfb6eSInès Varhol static uint64_t stm32l4x5_gpio_read(void *opaque, hwaddr addr,
3431cdcfb6eSInès Varhol                                     unsigned int size)
3441cdcfb6eSInès Varhol {
3451cdcfb6eSInès Varhol     Stm32l4x5GpioState *s = opaque;
3461cdcfb6eSInès Varhol 
3471cdcfb6eSInès Varhol     trace_stm32l4x5_gpio_read(s->name, addr);
3481cdcfb6eSInès Varhol 
3491cdcfb6eSInès Varhol     switch (addr) {
3501cdcfb6eSInès Varhol     case GPIO_MODER:
3511cdcfb6eSInès Varhol         return s->moder;
3521cdcfb6eSInès Varhol     case GPIO_OTYPER:
3531cdcfb6eSInès Varhol         return s->otyper;
3541cdcfb6eSInès Varhol     case GPIO_OSPEEDR:
3551cdcfb6eSInès Varhol         return s->ospeedr;
3561cdcfb6eSInès Varhol     case GPIO_PUPDR:
3571cdcfb6eSInès Varhol         return s->pupdr;
3581cdcfb6eSInès Varhol     case GPIO_IDR:
3591cdcfb6eSInès Varhol         return s->idr;
3601cdcfb6eSInès Varhol     case GPIO_ODR:
3611cdcfb6eSInès Varhol         return s->odr;
3621cdcfb6eSInès Varhol     case GPIO_BSRR:
3631cdcfb6eSInès Varhol         return 0;
3641cdcfb6eSInès Varhol     case GPIO_LCKR:
3651cdcfb6eSInès Varhol         return s->lckr;
3661cdcfb6eSInès Varhol     case GPIO_AFRL:
3671cdcfb6eSInès Varhol         return s->afrl;
3681cdcfb6eSInès Varhol     case GPIO_AFRH:
3691cdcfb6eSInès Varhol         return s->afrh;
3701cdcfb6eSInès Varhol     case GPIO_BRR:
3711cdcfb6eSInès Varhol         return 0;
3721cdcfb6eSInès Varhol     case GPIO_ASCR:
3731cdcfb6eSInès Varhol         return s->ascr;
3741cdcfb6eSInès Varhol     default:
3751cdcfb6eSInès Varhol         qemu_log_mask(LOG_GUEST_ERROR,
3761cdcfb6eSInès Varhol                       "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
3771cdcfb6eSInès Varhol         return 0;
3781cdcfb6eSInès Varhol     }
3791cdcfb6eSInès Varhol }
3801cdcfb6eSInès Varhol 
3811cdcfb6eSInès Varhol static const MemoryRegionOps stm32l4x5_gpio_ops = {
3821cdcfb6eSInès Varhol     .read = stm32l4x5_gpio_read,
3831cdcfb6eSInès Varhol     .write = stm32l4x5_gpio_write,
3841cdcfb6eSInès Varhol     .endianness = DEVICE_NATIVE_ENDIAN,
3851cdcfb6eSInès Varhol     .impl = {
3861cdcfb6eSInès Varhol         .min_access_size = 4,
3871cdcfb6eSInès Varhol         .max_access_size = 4,
3881cdcfb6eSInès Varhol         .unaligned = false,
3891cdcfb6eSInès Varhol     },
3901cdcfb6eSInès Varhol     .valid = {
3911cdcfb6eSInès Varhol         .min_access_size = 4,
3921cdcfb6eSInès Varhol         .max_access_size = 4,
3931cdcfb6eSInès Varhol         .unaligned = false,
3941cdcfb6eSInès Varhol     },
3951cdcfb6eSInès Varhol };
3961cdcfb6eSInès Varhol 
3971cdcfb6eSInès Varhol static void stm32l4x5_gpio_init(Object *obj)
3981cdcfb6eSInès Varhol {
3991cdcfb6eSInès Varhol     Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj);
4001cdcfb6eSInès Varhol 
4011cdcfb6eSInès Varhol     memory_region_init_io(&s->mmio, obj, &stm32l4x5_gpio_ops, s,
4021cdcfb6eSInès Varhol                           TYPE_STM32L4X5_GPIO, 0x400);
4031cdcfb6eSInès Varhol 
4041cdcfb6eSInès Varhol     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
4051cdcfb6eSInès Varhol 
4061cdcfb6eSInès Varhol     qdev_init_gpio_out(DEVICE(obj), s->pin, GPIO_NUM_PINS);
4071cdcfb6eSInès Varhol     qdev_init_gpio_in(DEVICE(obj), stm32l4x5_gpio_set, GPIO_NUM_PINS);
4081cdcfb6eSInès Varhol 
4091cdcfb6eSInès Varhol     s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0);
4101cdcfb6eSInès Varhol 
4111cdcfb6eSInès Varhol     object_property_add(obj, "disconnected-pins", "uint16",
4121cdcfb6eSInès Varhol                         disconnected_pins_get, disconnected_pins_set,
4131cdcfb6eSInès Varhol                         NULL, &s->disconnected_pins);
4141cdcfb6eSInès Varhol     object_property_add(obj, "clock-freq-hz", "uint32",
4151cdcfb6eSInès Varhol                         clock_freq_get, NULL, NULL, NULL);
4161cdcfb6eSInès Varhol }
4171cdcfb6eSInès Varhol 
4181cdcfb6eSInès Varhol static void stm32l4x5_gpio_realize(DeviceState *dev, Error **errp)
4191cdcfb6eSInès Varhol {
4201cdcfb6eSInès Varhol     Stm32l4x5GpioState *s = STM32L4X5_GPIO(dev);
4211cdcfb6eSInès Varhol     if (!clock_has_source(s->clk)) {
4221cdcfb6eSInès Varhol         error_setg(errp, "GPIO: clk input must be connected");
4231cdcfb6eSInès Varhol         return;
4241cdcfb6eSInès Varhol     }
4251cdcfb6eSInès Varhol }
4261cdcfb6eSInès Varhol 
4271cdcfb6eSInès Varhol static const VMStateDescription vmstate_stm32l4x5_gpio = {
4281cdcfb6eSInès Varhol     .name = TYPE_STM32L4X5_GPIO,
4291cdcfb6eSInès Varhol     .version_id = 1,
4301cdcfb6eSInès Varhol     .minimum_version_id = 1,
4311cdcfb6eSInès Varhol     .fields = (VMStateField[]){
4321cdcfb6eSInès Varhol         VMSTATE_UINT32(moder, Stm32l4x5GpioState),
4331cdcfb6eSInès Varhol         VMSTATE_UINT32(otyper, Stm32l4x5GpioState),
4341cdcfb6eSInès Varhol         VMSTATE_UINT32(ospeedr, Stm32l4x5GpioState),
4351cdcfb6eSInès Varhol         VMSTATE_UINT32(pupdr, Stm32l4x5GpioState),
4361cdcfb6eSInès Varhol         VMSTATE_UINT32(idr, Stm32l4x5GpioState),
4371cdcfb6eSInès Varhol         VMSTATE_UINT32(odr, Stm32l4x5GpioState),
4381cdcfb6eSInès Varhol         VMSTATE_UINT32(lckr, Stm32l4x5GpioState),
4391cdcfb6eSInès Varhol         VMSTATE_UINT32(afrl, Stm32l4x5GpioState),
4401cdcfb6eSInès Varhol         VMSTATE_UINT32(afrh, Stm32l4x5GpioState),
4411cdcfb6eSInès Varhol         VMSTATE_UINT32(ascr, Stm32l4x5GpioState),
4421cdcfb6eSInès Varhol         VMSTATE_UINT16(disconnected_pins, Stm32l4x5GpioState),
4431cdcfb6eSInès Varhol         VMSTATE_UINT16(pins_connected_high, Stm32l4x5GpioState),
4441cdcfb6eSInès Varhol         VMSTATE_END_OF_LIST()
4451cdcfb6eSInès Varhol     }
4461cdcfb6eSInès Varhol };
4471cdcfb6eSInès Varhol 
4481cdcfb6eSInès Varhol static Property stm32l4x5_gpio_properties[] = {
4491cdcfb6eSInès Varhol     DEFINE_PROP_STRING("name", Stm32l4x5GpioState, name),
4501cdcfb6eSInès Varhol     DEFINE_PROP_UINT32("mode-reset", Stm32l4x5GpioState, moder_reset, 0),
4511cdcfb6eSInès Varhol     DEFINE_PROP_UINT32("ospeed-reset", Stm32l4x5GpioState, ospeedr_reset, 0),
4521cdcfb6eSInès Varhol     DEFINE_PROP_UINT32("pupd-reset", Stm32l4x5GpioState, pupdr_reset, 0),
4531cdcfb6eSInès Varhol     DEFINE_PROP_END_OF_LIST(),
4541cdcfb6eSInès Varhol };
4551cdcfb6eSInès Varhol 
4561cdcfb6eSInès Varhol static void stm32l4x5_gpio_class_init(ObjectClass *klass, void *data)
4571cdcfb6eSInès Varhol {
4581cdcfb6eSInès Varhol     DeviceClass *dc = DEVICE_CLASS(klass);
4591cdcfb6eSInès Varhol     ResettableClass *rc = RESETTABLE_CLASS(klass);
4601cdcfb6eSInès Varhol 
4611cdcfb6eSInès Varhol     device_class_set_props(dc, stm32l4x5_gpio_properties);
4621cdcfb6eSInès Varhol     dc->vmsd = &vmstate_stm32l4x5_gpio;
4631cdcfb6eSInès Varhol     dc->realize = stm32l4x5_gpio_realize;
4641cdcfb6eSInès Varhol     rc->phases.hold = stm32l4x5_gpio_reset_hold;
4651cdcfb6eSInès Varhol }
4661cdcfb6eSInès Varhol 
4671cdcfb6eSInès Varhol static const TypeInfo stm32l4x5_gpio_types[] = {
4681cdcfb6eSInès Varhol     {
4691cdcfb6eSInès Varhol         .name = TYPE_STM32L4X5_GPIO,
4701cdcfb6eSInès Varhol         .parent = TYPE_SYS_BUS_DEVICE,
4711cdcfb6eSInès Varhol         .instance_size = sizeof(Stm32l4x5GpioState),
4721cdcfb6eSInès Varhol         .instance_init = stm32l4x5_gpio_init,
4731cdcfb6eSInès Varhol         .class_init = stm32l4x5_gpio_class_init,
4741cdcfb6eSInès Varhol     },
4751cdcfb6eSInès Varhol };
4761cdcfb6eSInès Varhol 
4771cdcfb6eSInès Varhol DEFINE_TYPES(stm32l4x5_gpio_types)
478