/* * ESP32 GPIO emulation * * Copyright (c) 2019 Espressif Systems (Shanghai) Co. Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 or * (at your option) any later version. */ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/registerfields.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/gpio/esp32_gpio.h" #define GPIO_OUT 0x04 #define GPIO_OUT_W1TS 0x08 #define GPIO_OUT_W1TC 0x0c #define GPIO_ENABLE 0x20 #define GPIO_ENABLE_W1TS 0x24 #define GPIO_ENABLE_W1TC 0x28 #define GPIO_IN 0x3c #define GPIO_STATUS 0x44 #define GPIO_STATUS_W1TS 0x48 #define GPIO_STATUS_W1TC 0x4c #define GPIO_PCPU_INT 0x5c #define GPIO_PIN_INT_TYPE_SHIFT 7 #define GPIO_PIN_INT_TYPE_MASK 0x7 #define GPIO_PIN_INT_ENA_SHIFT 13 #define GPIO_PIN_INT_ENA_CPU0 BIT(0) #define GPIO_INTR_DISABLE 0 #define GPIO_INTR_POSEDGE 1 #define GPIO_INTR_NEGEDGE 2 #define GPIO_INTR_ANYEDGE 3 #define GPIO_INTR_LOW_LEVEL 4 #define GPIO_INTR_HIGH_LEVEL 5 static void esp32_gpio_drive_outputs(Esp32GpioState *s) { for (int i = 0; i < ESP32_GPIO_COUNT; i++) { int level = (s->output_enable & BIT(i)) ? !!(s->output_level & BIT(i)) : 1; qemu_set_irq(s->output_lines[i], level); } } static uint32_t esp32_gpio_enabled_interrupts(Esp32GpioState *s) { uint32_t enabled = 0; for (int pin = 0; pin < ESP32_GPIO_COUNT; pin++) { uint32_t interrupt_enable = s->pin_config[pin] >> GPIO_PIN_INT_ENA_SHIFT; if (interrupt_enable & GPIO_PIN_INT_ENA_CPU0) { enabled |= BIT(pin); } } return enabled; } static void esp32_gpio_update_irq(Esp32GpioState *s) { qemu_set_irq(s->irq, !!(s->interrupt_status & esp32_gpio_enabled_interrupts(s))); } static void esp32_gpio_latch_level_interrupts(Esp32GpioState *s) { uint32_t levels = qatomic_read(&s->input_level); for (int pin = 0; pin < ESP32_GPIO_COUNT; pin++) { uint32_t type = extract32(s->pin_config[pin], GPIO_PIN_INT_TYPE_SHIFT, 3); bool level = !!(levels & BIT(pin)); if ((type == GPIO_INTR_LOW_LEVEL && !level) || (type == GPIO_INTR_HIGH_LEVEL && level)) { s->interrupt_status |= BIT(pin); } } } void esp32_gpio_set_input_level(Esp32GpioState *s, int pin, bool level) { uint32_t old_level; uint32_t new_level; do { old_level = qatomic_read(&s->input_level); new_level = deposit32(old_level, pin, 1, level); } while (qatomic_cmpxchg(&s->input_level, old_level, new_level) != old_level); bool old_pin_level = !!(old_level & BIT(pin)); uint32_t type = extract32(s->pin_config[pin], GPIO_PIN_INT_TYPE_SHIFT, 3); if ((!old_pin_level && level && type == GPIO_INTR_POSEDGE) || (old_pin_level && !level && type == GPIO_INTR_NEGEDGE) || (old_pin_level != level && type == GPIO_INTR_ANYEDGE) || (!level && type == GPIO_INTR_LOW_LEVEL) || (level && type == GPIO_INTR_HIGH_LEVEL)) { s->interrupt_status |= BIT(pin); } esp32_gpio_update_irq(s); } static void esp32_gpio_set_input(void *opaque, int pin, int level) { esp32_gpio_set_input_level(ESP32_GPIO(opaque), pin, level); } static void esp32_gpio_get_input(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { Esp32GpioState *s = ESP32_GPIO(obj); int pin = GPOINTER_TO_INT(opaque); bool level = extract32(qatomic_read(&s->input_level), pin, 1); visit_type_bool(v, name, &level, errp); } static void esp32_gpio_set_input_property(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { Esp32GpioState *s = ESP32_GPIO(obj); int pin = GPOINTER_TO_INT(opaque); bool level; if (visit_type_bool(v, name, &level, errp)) { esp32_gpio_set_input(s, pin, level); } } static uint64_t esp32_gpio_read(void *opaque, hwaddr addr, unsigned int size) { Esp32GpioState *s = ESP32_GPIO(opaque); switch (addr) { case GPIO_OUT: return s->output_level; case GPIO_ENABLE: return s->output_enable; case A_GPIO_STRAP: return s->strap_mode; case GPIO_IN: return qatomic_read(&s->input_level); case GPIO_STATUS: return s->interrupt_status; case GPIO_PCPU_INT: return s->interrupt_status & esp32_gpio_enabled_interrupts(s); default: if (addr >= s->pin_config_base && addr < s->pin_config_base + sizeof(s->pin_config)) { return s->pin_config[(addr - s->pin_config_base) / 4]; } return 0; } } static void esp32_gpio_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size) { Esp32GpioState *s = ESP32_GPIO(opaque); if (addr >= s->pin_config_base && addr < s->pin_config_base + sizeof(s->pin_config)) { s->pin_config[(addr - s->pin_config_base) / 4] = value; esp32_gpio_latch_level_interrupts(s); esp32_gpio_update_irq(s); return; } switch (addr) { case GPIO_OUT: s->output_level = value; break; case GPIO_OUT_W1TS: s->output_level |= value; break; case GPIO_OUT_W1TC: s->output_level &= ~value; break; case GPIO_ENABLE: s->output_enable = value; break; case GPIO_ENABLE_W1TS: s->output_enable |= value; break; case GPIO_ENABLE_W1TC: s->output_enable &= ~value; break; case GPIO_STATUS: s->interrupt_status = value; esp32_gpio_update_irq(s); return; case GPIO_STATUS_W1TS: s->interrupt_status |= value; esp32_gpio_update_irq(s); return; case GPIO_STATUS_W1TC: s->interrupt_status &= ~value; esp32_gpio_latch_level_interrupts(s); esp32_gpio_update_irq(s); return; default: return; } esp32_gpio_drive_outputs(s); } static const MemoryRegionOps uart_ops = { .read = esp32_gpio_read, .write = esp32_gpio_write, .endianness = DEVICE_LITTLE_ENDIAN, }; static void esp32_gpio_reset_hold(Object *obj, ResetType type) { Esp32GpioState *s = ESP32_GPIO(obj); s->input_level = UINT32_MAX; s->output_level = 0; s->output_enable = 0; s->interrupt_status = 0; memset(s->pin_config, 0, sizeof(s->pin_config)); esp32_gpio_drive_outputs(s); esp32_gpio_update_irq(s); } static void esp32_gpio_realize(DeviceState *dev, Error **errp) { } static void esp32_gpio_init(Object *obj) { Esp32GpioState *s = ESP32_GPIO(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); /* Set the default value for the strap_mode property */ object_property_set_int(obj, "strap_mode", ESP32_STRAP_MODE_FLASH_BOOT, &error_fatal); s->pin_config_base = 0x88; memory_region_init_io(&s->iomem, obj, &uart_ops, s, TYPE_ESP32_GPIO, 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); qdev_init_gpio_in_named(DEVICE(obj), esp32_gpio_set_input, ESP32_GPIO_INPUT, ESP32_GPIO_COUNT); qdev_init_gpio_out_named(DEVICE(obj), s->output_lines, ESP32_GPIO_OUTPUT, ESP32_GPIO_COUNT); for (int i = 0; i < ESP32_GPIO_COUNT; i++) { char *name = g_strdup_printf("input-level[%d]", i); object_property_add(obj, name, "bool", esp32_gpio_get_input, esp32_gpio_set_input_property, NULL, GINT_TO_POINTER(i)); g_free(name); } esp32_gpio_reset_hold(obj, RESET_TYPE_COLD); } static Property esp32_gpio_properties[] = { /* The strap_mode needs to be explicitly set in the instance init, thus, set * the default value to 0. */ DEFINE_PROP_UINT32("strap_mode", Esp32GpioState, strap_mode, 0), DEFINE_PROP_END_OF_LIST(), }; static void esp32_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); rc->phases.hold = esp32_gpio_reset_hold; dc->realize = esp32_gpio_realize; device_class_set_props(dc, esp32_gpio_properties); } static const TypeInfo esp32_gpio_info = { .name = TYPE_ESP32_GPIO, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Esp32GpioState), .instance_init = esp32_gpio_init, .class_init = esp32_gpio_class_init, .class_size = sizeof(Esp32GpioClass), }; static void esp32_gpio_register_types(void) { type_register_static(&esp32_gpio_info); } type_init(esp32_gpio_register_types)