/* * 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. * Modifed by cpu. */ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/error-report.h" #include "qapi/error.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" //base address is 0x3FF44000 #define GPIO_OUT_REG_RW 0x0004 #define GPIO_OUT_W1TS_REG_WO 0x0008 #define GPIO_OUT_W1TC_REG_WO 0x000C #define GPIO_OUT1_REG_RW 0x0010 #define GPIO_OUT1_W1TS_REG_WO 0x0014 #define GPIO_OUT1_W1TC_REG_WO 0x0018 #define GPIO_ENABLE_REG_RW 0x0020 #define GPIO_ENABLE_W1TS_REG_WO 0x0024 #define GPIO_ENABLE_W1TC_REG_WO 0x0028 #define GPIO_ENABLE1_REG_RW 0x002C #define GPIO_ENABLE1_W1TS_REG_WO 0x0030 #define GPIO_ENABLE1_W1TC_REG_WO 0x0034 #define GPIO_STRAP_REG_RO 0x0038 #define GPIO_IN_REG_RO 0x003C #define GPIO_IN1_REG_RO 0x0040 #define GPIO_STATUS_REG_RW 0x0044 #define GPIO_STATUS_W1TS_REG_WO 0x0048 #define GPIO_STATUS_W1TC_REG_WO 0x004C #define GPIO_STATUS1_REG_RW 0x0050 #define GPIO_STATUS1_W1TS_REG_WO 0x0054 #define GPIO_STATUS1_W1TC_REG_WO 0x0058 #define GPIO_ACPU_INT_REG_RO 0x0060 #define GPIO_ACPU_NMI_INT_REG_RO 0x0064 #define GPIO_PCPU_INT_REG_RO 0x0068 #define GPIO_PCPU_NMI_INT_REG_RO 0x006C #define GPIO_ACPU_INT1_REG_RO 0x0074 #define GPIO_ACPU_NMI_INT1_REG_RO 0x0078 #define GPIO_PCPU_INT1_REG_RO 0x007C #define GPIO_PCPU_NMI_INT1_REG_RO 0x0080 #define GPIO_PIN_REG_START_RW 0x0088 #define GPIO_PIN_REG_END_RW 0x0124 #define GPIO_FUNC_IN_SEL_CFG_REG_START_RW 0x0130 #define GPIO_FUNC_IN_SEL_CFG_REG_END_RW 0x052C #define GPIO_FUNC_OUT_SEL_CFG_START_RW 0x0530 #define GPIO_FUNC_OUT_SEL_CFG_END_RW 0x092C #define GPIO_INT_TYPE_DISABLED 0 #define GPIO_INT_TYPE_RISING_EDGE 1 #define GPIO_INT_TYPE_FALLING_EDGE 2 #define GPIO_INT_TYPE_ANY_EDGE 3 #define GPIO_INT_TYPE_LOW_LEVEL 4 #define GPIO_INT_TYPE_HIGH_LEVEL 5 /***************************************** * GPIO IN IRQ CALLBACK * *****************************************/ static void inputGpioHandler( void* opaque, int gpioNo, int level ){ int oldLevel; int bankNo; unsigned char flagIrq; unsigned char flagNmiIrq; uint32_t mask; Esp32GpioState* gpioState; //just in case if( gpioNo < 0 ) return; if( gpioNo >= ESP32_GPIO_COUNT ) return; gpioState = ESP32_GPIO( opaque ); //prepage vars for operate with registers if( gpioNo < 32 ){ bankNo = 0; mask = 1ull << gpioNo; }else{ bankNo = 1; mask = 1ull << ( gpioNo - 32 ); } //get current gpio level oldLevel = ( mask & gpioState->inputRegister[bankNo] ) ? 1 : 0; //set new gpio level if( level ) gpioState->inputRegister[bankNo] |= mask; else gpioState->inputRegister[bankNo] &= ~mask; //printf( "[%s] GPIO %i old value: %i, new value: %i\n", __FUNCTION__, gpioNo, oldLevel, level ); //TODO: gpio inversion function //interrupt trigger check switch( gpioState->gpioConfig[gpioNo].interruptType ){ case GPIO_INT_TYPE_DISABLED: return; case GPIO_INT_TYPE_RISING_EDGE: if( oldLevel < level ) goto l_interruptTriggered; return; case GPIO_INT_TYPE_FALLING_EDGE: if( oldLevel > level ) goto l_interruptTriggered; return; case GPIO_INT_TYPE_ANY_EDGE: if( oldLevel != level ) goto l_interruptTriggered; return; case GPIO_INT_TYPE_LOW_LEVEL: if( !level ) goto l_interruptTriggered; return; case GPIO_INT_TYPE_HIGH_LEVEL: if( level ) goto l_interruptTriggered; return; default: qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unknown interrupt type (%" PRIu32 ") has been ignored\n", __FUNCTION__, gpioState->gpioConfig[gpioNo].interruptType ); return; } l_interruptTriggered: flagIrq = 0; flagNmiIrq = 0; //update interrupt status register gpioState->interruptStatusRegister[bankNo] |= mask; //update app cpu interrupt register if( gpioState->gpioConfig[gpioNo].appCpuIntEnable ){ gpioState->appCpuInterruptStatus[bankNo] |= mask; flagIrq = 1; } //update app cpu non-maskable interrupt register if( gpioState->gpioConfig[gpioNo].appCpuNmiEnable ){ gpioState->appCpuNmiInterruptStatus[bankNo] |= mask; flagNmiIrq = 1; } //update pro cpu interrupt register if( gpioState->gpioConfig[gpioNo].proCpuIntEnable ){ gpioState->proCpuInterruptStatus[bankNo] |= mask; flagIrq = 1; } //update pro cpu non-maskable interrupt register if( gpioState->gpioConfig[gpioNo].proCpuNmiEnable ){ gpioState->proCpuNmiInterruptStatus[bankNo] |= mask; flagNmiIrq = 1; } //apply irq flags to irq lines if( flagIrq ) qemu_set_irq( gpioState->gpioIrq, 1 ); if( flagNmiIrq ) qemu_set_irq( gpioState->gpioNmiIrq, 1 ); } /*************************************** * GPIO READ CALLBACK * ***************************************/ static uint64_t esp32GpioRead( void* opaque, hwaddr address, unsigned int size ){ size_t index; Esp32GpioState* gpioState; gpioState = ESP32_GPIO( opaque ); //unaligned access if( address % 4 ){ qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unaligned access: 0x%04lX\n", __FUNCTION__, address ); return 0; } //unexpected size if( size != 4 ){ qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unexpected size: %u\n", __FUNCTION__, size ); return 0; } //output register if( address == GPIO_OUT_REG_RW ) return gpioState->outputRegister[0]; if( address == GPIO_OUT1_REG_RW ) return gpioState->outputRegister[1]; //enable output register if( address == GPIO_ENABLE_REG_RW ) return gpioState->outputEnableRegister[0]; if( address == GPIO_ENABLE1_REG_RW ) return gpioState->outputEnableRegister[1]; //bootstrap pin value if( address == GPIO_STRAP_REG_RO ) return gpioState->strap_mode; //input register if( address == GPIO_IN_REG_RO ) return gpioState->inputRegister[0]; if( address == GPIO_IN1_REG_RO ) return gpioState->inputRegister[1]; //interrupt register if( address == GPIO_STATUS_REG_RW ) return gpioState->interruptStatusRegister[0]; if( address == GPIO_STATUS1_REG_RW ) return gpioState->interruptStatusRegister[1]; //application cpu interrupt 0-31 if( address == GPIO_ACPU_INT_REG_RO ) return gpioState->appCpuInterruptStatus[0]; if( address == GPIO_ACPU_NMI_INT_REG_RO ) return gpioState->appCpuNmiInterruptStatus[0]; //pro cpu interrupt 0-31 if( address == GPIO_PCPU_INT_REG_RO ) return gpioState->proCpuInterruptStatus[0]; if( address == GPIO_PCPU_NMI_INT_REG_RO ) return gpioState->proCpuNmiInterruptStatus[0]; //application cpu interrupt 32-39 if( address == GPIO_ACPU_INT1_REG_RO ) return gpioState->appCpuInterruptStatus[1]; if( address == GPIO_ACPU_NMI_INT1_REG_RO ) return gpioState->appCpuNmiInterruptStatus[1]; //pro cpu interrupt 32-39 if( address == GPIO_PCPU_INT1_REG_RO ) return gpioState->proCpuInterruptStatus[1]; if( address == GPIO_PCPU_NMI_INT1_REG_RO ) return gpioState->proCpuNmiInterruptStatus[1]; //pins configuration if( address >= GPIO_PIN_REG_START_RW && address <= GPIO_PIN_REG_END_RW ){ index = ( address - GPIO_PIN_REG_START_RW ) / 4; return gpioState->gpioConfig[index].mem; } //peripheral input function selection if( address >= GPIO_FUNC_IN_SEL_CFG_REG_START_RW && address <= GPIO_FUNC_IN_SEL_CFG_REG_END_RW ){ index = ( address - GPIO_FUNC_IN_SEL_CFG_REG_START_RW ) / 4; return gpioState->peripheralFunctionInput[index]; } //peripheral output function selection if( address >= GPIO_FUNC_OUT_SEL_CFG_START_RW && address <= GPIO_FUNC_OUT_SEL_CFG_END_RW ){ index = ( address - GPIO_FUNC_OUT_SEL_CFG_START_RW ) / 4; return gpioState->peripheralFunctionOutput[index]; } qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unexpected address: 0x%04lX\n", __FUNCTION__, address ); return 0; } /**************************************** * GPIO WRITE CALLBACK * ****************************************/ //write output register logic static void writeOutputRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t value ){ gpioState->outputRegister[index] = value; } static void setBitOutputRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t mask ){ gpioState->outputRegister[index] |= mask; } static void clearBitOutputRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t mask ){ gpioState->outputRegister[index] &= ~mask; } //write output enable register logic static void writeOutputEnableRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t value ){ gpioState->outputEnableRegister[index] = value; } static void setBitOutputEnableRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t mask ){ gpioState->outputEnableRegister[index] |= mask; } static void clearBitOutputEnableRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t mask ){ gpioState->outputEnableRegister[index] &= ~mask; } //write interrupt status register logic static void writeInterruptStatusRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t value ){ qemu_log_mask( LOG_GUEST_ERROR, "[%s] In not fully implemented!\n", __FUNCTION__ ); gpioState->interruptStatusRegister[index] = value; } static void setBitInterruptStatusRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t mask ){ qemu_log_mask( LOG_GUEST_ERROR, "[%s] In not fully implemented!\n", __FUNCTION__ ); gpioState->interruptStatusRegister[index] |= mask; } static void clearBitInterruptStatusRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t mask ){ gpioState->interruptStatusRegister[index] &= ~mask; gpioState->appCpuInterruptStatus[index] &= ~mask; gpioState->appCpuNmiInterruptStatus[index] &= ~mask; gpioState->proCpuInterruptStatus[index] &= ~mask; gpioState->proCpuNmiInterruptStatus[index] &= ~mask; if( !gpioState->appCpuInterruptStatus[0] && !gpioState->appCpuInterruptStatus[1] && !gpioState->proCpuInterruptStatus[0] && !gpioState->proCpuInterruptStatus[1] ) qemu_set_irq( gpioState->gpioIrq, 0 ); if( !gpioState->appCpuNmiInterruptStatus[0] && !gpioState->appCpuNmiInterruptStatus[1] && !gpioState->proCpuNmiInterruptStatus[0] && !gpioState->proCpuNmiInterruptStatus[1] ) qemu_set_irq( gpioState->gpioNmiIrq, 0 ); } //gpio set config logic static void setGpioConfig( Esp32GpioState* gpioState, unsigned int index, uint32_t value ){ gpioState->gpioConfig[index].mem = value; } //set peripheral input function logic static void setPeripheralInputFunction( Esp32GpioState* gpioState, unsigned int index, uint32_t value ){ gpioState->peripheralFunctionInput[index] = value; } //set peripheral output function logic static void setPeripheralOutputFunction( Esp32GpioState* gpioState, unsigned int index, uint32_t value ){ gpioState->peripheralFunctionOutput[index] = value; } static void esp32GpioWrite( void* opaque, hwaddr address, uint64_t value, unsigned int size ){ size_t index; Esp32GpioState* gpioState; gpioState = ESP32_GPIO( opaque ); //unaligned access if( address % 4 ){ qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unaligned access: 0x%04lX\n", __FUNCTION__, address ); return; } //unexpected size if( size != 4 ){ qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unexpected size: %u\n", __FUNCTION__, size ); return; } //output register 0-31 if( address == GPIO_OUT_REG_RW ) { writeOutputRegister( gpioState, 0, value ); return; } if( address == GPIO_OUT_W1TS_REG_WO ) { setBitOutputRegister( gpioState, 0, value ); return; } if( address == GPIO_OUT_W1TC_REG_WO ) { clearBitOutputRegister( gpioState, 0, value ); return; } //output register 32-39 if( address == GPIO_OUT1_REG_RW ) { writeOutputRegister( gpioState, 1, value ); return; } if( address == GPIO_OUT1_W1TS_REG_WO ) { setBitOutputRegister( gpioState, 1, value ); return; } if( address == GPIO_OUT1_W1TC_REG_WO ) { clearBitOutputRegister( gpioState, 1, value ); return; } //output enable register 0-31 if( address == GPIO_ENABLE_REG_RW ) { writeOutputEnableRegister( gpioState, 0, value ); return; } if( address == GPIO_ENABLE_W1TS_REG_WO ) { setBitOutputEnableRegister( gpioState, 0, value ); return; } if( address == GPIO_ENABLE_W1TC_REG_WO ) { clearBitOutputEnableRegister( gpioState, 0, value ); return; } //output enable register 32-39 if( address == GPIO_ENABLE1_REG_RW ) { writeOutputEnableRegister( gpioState, 1, value ); return; } if( address == GPIO_ENABLE1_W1TS_REG_WO ) { setBitOutputEnableRegister( gpioState, 1, value ); return; } if( address == GPIO_ENABLE1_W1TC_REG_WO ) { clearBitOutputEnableRegister( gpioState, 1, value ); return; } //interrupt status register 0-31 if( address == GPIO_STATUS_REG_RW ) { writeInterruptStatusRegister( gpioState, 0, value ); return; } if( address == GPIO_STATUS_W1TS_REG_WO ) { setBitInterruptStatusRegister( gpioState, 0, value ); return; } if( address == GPIO_STATUS_W1TC_REG_WO ) { clearBitInterruptStatusRegister( gpioState, 0, value ); return; } //interrupt status register 32-39 if( address == GPIO_STATUS1_REG_RW ) { writeInterruptStatusRegister( gpioState, 1, value ); return; } if( address == GPIO_STATUS1_W1TS_REG_WO ) { setBitInterruptStatusRegister( gpioState, 1, value ); return; } if( address == GPIO_STATUS1_W1TC_REG_WO ) { clearBitInterruptStatusRegister( gpioState, 1, value ); return; } //pins configuration if( address >= GPIO_PIN_REG_START_RW && address <= GPIO_PIN_REG_END_RW ){ index = ( address - GPIO_PIN_REG_START_RW ) / 4; setGpioConfig( gpioState, index, value ); return; } //peripheral input function selection if( address >= GPIO_FUNC_IN_SEL_CFG_REG_START_RW && address <= GPIO_FUNC_IN_SEL_CFG_REG_END_RW ){ index = ( address - GPIO_FUNC_IN_SEL_CFG_REG_START_RW ) / 4; setPeripheralInputFunction( gpioState, index, value ); return; } //peripheral output function selection if( address >= GPIO_FUNC_OUT_SEL_CFG_START_RW && address <= GPIO_FUNC_OUT_SEL_CFG_END_RW ){ index = ( address - GPIO_FUNC_OUT_SEL_CFG_START_RW ) / 4; setPeripheralOutputFunction( gpioState, index, value ); return; } qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unexpected address: 0x%04lX\n", __FUNCTION__, address ); } /******************************* * CLASS INIT * *******************************/ static const MemoryRegionOps uart_ops = { .read = esp32GpioRead, .write = esp32GpioWrite, .endianness = DEVICE_LITTLE_ENDIAN, }; static void esp32_gpio_reset_hold(Object *obj, ResetType type) { } 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); //deviceState added by cpu DeviceState* deviceState; /* Set the default value for the strap_mode property */ object_property_set_int(obj, "strap_mode", ESP32_STRAP_MODE_FLASH_BOOT, &error_fatal); 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); //added by cpu deviceState = DEVICE( s ); qdev_init_gpio_in_named_with_opaque( deviceState, inputGpioHandler, s, "gpio_in", ESP32_GPIO_COUNT ); qdev_init_gpio_out_named( deviceState, &s->gpioIrq, ESP32_GPIO_IRQ_GPIO, 1 ); qdev_init_gpio_out_named( deviceState, &s->gpioNmiIrq, ESP32_GPIO_NMI_IRQ_GPIO, 1 ); } 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); } /****************************** * TYPE INFO * ******************************/ 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)