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 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 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 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 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 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 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 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 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 */ 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 */ 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 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 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 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 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 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 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 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 450*de531a6bSRichard Henderson static const 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 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