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