Files
qemu-xteink/hw/gpio/esp32_gpio.c
T

302 lines
8.9 KiB
C

/*
* 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)