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