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