diff --git a/.gitignore b/.gitignore index 0c5af83aa7454..4e3eed1653f86 100644 --- a/.gitignore +++ b/.gitignore @@ -161,3 +161,7 @@ trace-dtrace-root.dtrace trace-ust-all.h trace-ust-all.c /target/arm/decode-sve.inc.c +storage-daemon/ +qemu-storage-daemon +roms +/build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000..f14c2cc6f1436 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.3) +project(qemu_dev) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +file(GLOB_RECURSE src "*.c" "*.h") + +set(SOURCES ${src}) +add_executable(qemu_dev ${SOURCES} ) +target_include_directories(qemu_dev PUBLIC "include" "target/arm" "") + +add_custom_target(qemu ALL + make -j4 + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build" + ) diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 534a6a119e5d7..a28df465c001e 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -54,3 +54,16 @@ obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o obj-$(CONFIG_FSL_IMX6UL) += fsl-imx6ul.o mcimx6ul-evk.o obj-$(CONFIG_NRF51_SOC) += nrf51_soc.o +obj-y += amazfit.o +obj-y += stm32l467_soc.o +obj-y += stm32l476_flash.o +obj-y += stm32l476_lptim.o +obj-y += stm32l476_pwr.o +obj-y += stm32l476_rcc.o +obj-y += stm32l476_dma.o +obj-y += stm32l476_spi.o +obj-y += stm32l476_qspi.o +obj-y += stm32l476_gpio.o +obj-y += stm32l476_syscfg.o +obj-y += stm32l476_i2c.o +obj-y += it7259.o diff --git a/hw/arm/amazfit.c b/hw/arm/amazfit.c new file mode 100644 index 0000000000000..5b8102fd554bf --- /dev/null +++ b/hw/arm/amazfit.c @@ -0,0 +1,57 @@ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "hw/arm/stm32l467_soc.h" +#include "hw/arm/boot.h" +#include "hw/loader.h" +#include "hw/ssi/ssi.h" +#include "hw/i2c/i2c.h" + +static uint32_t readu32(hwaddr addr) { + uint32_t ret; + ARMCPU *cpu = ARM_CPU(first_cpu); + cpu_memory_rw_debug(cpu, addr, &ret, 4, false); + + return ret; +} + +static void bip_init(MachineState *machine) { + + STM32L467State *dev = STM32L467_SOC(qdev_create(NULL, TYPE_STM32L467_SOC)); + object_property_set_bool(OBJECT(dev), true, "realized", &error_fatal); + + SSIBus* spi_bus = (SSIBus *)qdev_get_child_bus(&dev->spi[2], "ssi"); // SPI3 + DeviceState * display = ssi_create_slave_no_init(spi_bus, "jdi-lpm013m126c"); + qdev_init_nofail(display); + + /* --- QSPI Flash --------------------------------------------- */ + SSIBus * qspi = (SSIBus *)qdev_get_child_bus(&dev->qspi, "qspi"); + DeviceState *qspi_flash = ssi_create_slave_no_init(qspi, "w25q64fw"); + + DriveInfo *dinfo = drive_get(IF_MTD, 0, 0); + BlockBackend *blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; + qdev_prop_set_drive(qspi_flash, "drive", blk, &error_fatal); + qdev_init_nofail(qspi_flash); + + qemu_irq mx25u_cs = qdev_get_gpio_in_named(qspi_flash, SSI_GPIO_CS, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&dev->qspi), 1, mx25u_cs); + + load_image_targphys("/Users/Marijn/Downloads/bip/image.bin", +// load_image_targphys("/Users/Marijn/Projects/_pebble/Amazfitbip-FreeRTOS/bin/lcd_test.bin", + 0, + FLASH_SIZE); + + I2CBus *i2c = (I2CBus *) qdev_get_child_bus(&dev->i2c[0], "i2c"); + i2c_create_slave(i2c, "it7259", 0x46); + + armv7m_load_kernel(ARM_CPU(first_cpu), NULL, 0); +} + +static void bip_machine_init(MachineClass *mc) { + mc->desc = "Amazfit Bip"; + mc->init = bip_init; +} + +DEFINE_MACHINE("amazfitbip", bip_machine_init) diff --git a/hw/arm/it7259.c b/hw/arm/it7259.c new file mode 100644 index 0000000000000..7b57a44e0f0a1 --- /dev/null +++ b/hw/arm/it7259.c @@ -0,0 +1,74 @@ +#include "qemu/osdep.h" +#include "hw/i2c/i2c.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qemu/module.h" +#include "qemu/timer.h" +#include "ui/console.h" + +#define TYPE_IT7259 "it7259" +#define IT7259(obj) OBJECT_CHECK(IT7259State, (obj), TYPE_IT7259) + +typedef struct +{ + I2CSlave parent_obj; + +} IT7259State; + +static int +it7259_event(I2CSlave *i2c, enum i2c_event event) +{ + IT7259State *s = IT7259(i2c); + + return 0; +} + +static uint8_t +it7259_rx(I2CSlave *i2c) +{ + IT7259State *s = IT7259(i2c); + + return 0; +} + +static int +it7259_tx(I2CSlave *i2c, uint8_t data) +{ + IT7259State *s = IT7259(i2c); + printf("I2C: 0x%02X\n", data); + + return 0; +} + +static void +it7259_realize(DeviceState *dev, Error **errp) +{ + IT7259State *s = IT7259(dev); +} + +static void +it7259_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + + dc->realize = it7259_realize; + k->event = it7259_event; + k->recv = it7259_rx; + k->send = it7259_tx; +} + +static const TypeInfo it7259_info = { + .name = TYPE_IT7259, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(IT7259State), + .class_init = it7259_class_init, +}; + +static void +it7259_register_types(void) +{ + type_register_static(&it7259_info); +} + +type_init(it7259_register_types) diff --git a/hw/arm/stm32l467_soc.c b/hw/arm/stm32l467_soc.c new file mode 100644 index 0000000000000..c676916a05d19 --- /dev/null +++ b/hw/arm/stm32l467_soc.c @@ -0,0 +1,398 @@ + +#include "qemu/osdep.h" +#include "hw/misc/unimp.h" +#include "hw/sysbus.h" +#include "hw/arm/armv7m.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "hw/loader.h" +#include "qemu/log.h" + +#include "hw/arm/stm32l467_soc.h" +#include "hw/arm/stm32l476_gpio.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#define PERIPH_BASE 0x40000000UL +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL) +#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000UL) + +/*!< APB1 peripherals */ +#define TIM2_BASE (APB1PERIPH_BASE + 0x0000UL) +#define TIM3_BASE (APB1PERIPH_BASE + 0x0400UL) +#define TIM4_BASE (APB1PERIPH_BASE + 0x0800UL) +#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00UL) +#define TIM6_BASE (APB1PERIPH_BASE + 0x1000UL) +#define TIM7_BASE (APB1PERIPH_BASE + 0x1400UL) +#define LCD_BASE (APB1PERIPH_BASE + 0x2400UL) +#define RTC_BASE (APB1PERIPH_BASE + 0x2800UL) +#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00UL) +#define IWDG_BASE (APB1PERIPH_BASE + 0x3000UL) +#define SPI2_BASE (APB1PERIPH_BASE + 0x3800UL) +#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00UL) +#define USART2_BASE (APB1PERIPH_BASE + 0x4400UL) +#define USART3_BASE (APB1PERIPH_BASE + 0x4800UL) +#define UART4_BASE (APB1PERIPH_BASE + 0x4C00UL) +#define UART5_BASE (APB1PERIPH_BASE + 0x5000UL) +#define I2C1_BASE (APB1PERIPH_BASE + 0x5400UL) +#define I2C2_BASE (APB1PERIPH_BASE + 0x5800UL) +#define I2C3_BASE (APB1PERIPH_BASE + 0x5C00UL) +#define CAN1_BASE (APB1PERIPH_BASE + 0x6400UL) +#define PWR_BASE (APB1PERIPH_BASE + 0x7000UL) +#define DAC_BASE (APB1PERIPH_BASE + 0x7400UL) +#define DAC1_BASE (APB1PERIPH_BASE + 0x7400UL) +#define OPAMP_BASE (APB1PERIPH_BASE + 0x7800UL) +#define OPAMP1_BASE (APB1PERIPH_BASE + 0x7800UL) +#define OPAMP2_BASE (APB1PERIPH_BASE + 0x7810UL) +#define LPTIM1_BASE (APB1PERIPH_BASE + 0x7C00UL) +#define LPUART1_BASE (APB1PERIPH_BASE + 0x8000UL) +#define SWPMI1_BASE (APB1PERIPH_BASE + 0x8800UL) +#define LPTIM2_BASE (APB1PERIPH_BASE + 0x9400UL) + + +/*!< APB2 peripherals */ +#define SYSCFG_BASE (APB2PERIPH_BASE + 0x0000UL) +#define VREFBUF_BASE (APB2PERIPH_BASE + 0x0030UL) +#define COMP1_BASE (APB2PERIPH_BASE + 0x0200UL) +#define COMP2_BASE (APB2PERIPH_BASE + 0x0204UL) +#define EXTI_BASE (APB2PERIPH_BASE + 0x0400UL) +#define FIREWALL_BASE (APB2PERIPH_BASE + 0x1C00UL) +#define SDMMC1_BASE (APB2PERIPH_BASE + 0x2800UL) +#define TIM1_BASE (APB2PERIPH_BASE + 0x2C00UL) +#define SPI1_BASE (APB2PERIPH_BASE + 0x3000UL) +#define TIM8_BASE (APB2PERIPH_BASE + 0x3400UL) +#define USART1_BASE (APB2PERIPH_BASE + 0x3800UL) +#define TIM15_BASE (APB2PERIPH_BASE + 0x4000UL) +#define TIM16_BASE (APB2PERIPH_BASE + 0x4400UL) +#define TIM17_BASE (APB2PERIPH_BASE + 0x4800UL) +#define SAI1_BASE (APB2PERIPH_BASE + 0x5400UL) +#define SAI1_Block_A_BASE (SAI1_BASE + 0x0004UL) +#define SAI1_Block_B_BASE (SAI1_BASE + 0x0024UL) +#define SAI2_BASE (APB2PERIPH_BASE + 0x5800UL) +#define SAI2_Block_A_BASE (SAI2_BASE + 0x0004UL) +#define SAI2_Block_B_BASE (SAI2_BASE + 0x0024UL) +#define DFSDM1_BASE (APB2PERIPH_BASE + 0x6000UL) +#define DFSDM1_Channel0_BASE (DFSDM1_BASE + 0x0000UL) +#define DFSDM1_Channel1_BASE (DFSDM1_BASE + 0x0020UL) +#define DFSDM1_Channel2_BASE (DFSDM1_BASE + 0x0040UL) +#define DFSDM1_Channel3_BASE (DFSDM1_BASE + 0x0060UL) +#define DFSDM1_Channel4_BASE (DFSDM1_BASE + 0x0080UL) +#define DFSDM1_Channel5_BASE (DFSDM1_BASE + 0x00A0UL) +#define DFSDM1_Channel6_BASE (DFSDM1_BASE + 0x00C0UL) +#define DFSDM1_Channel7_BASE (DFSDM1_BASE + 0x00E0UL) +#define DFSDM1_Filter0_BASE (DFSDM1_BASE + 0x0100UL) +#define DFSDM1_Filter1_BASE (DFSDM1_BASE + 0x0180UL) +#define DFSDM1_Filter2_BASE (DFSDM1_BASE + 0x0200UL) +#define DFSDM1_Filter3_BASE (DFSDM1_BASE + 0x0280UL) + +/*!< AHB1 peripherals */ +#define DMA1_BASE (AHB1PERIPH_BASE) +#define DMA2_BASE (AHB1PERIPH_BASE + 0x0400UL) +#define RCC_BASE (AHB1PERIPH_BASE + 0x1000UL) +#define FLASH_R_BASE (AHB1PERIPH_BASE + 0x2000UL) +#define CRC_BASE (AHB1PERIPH_BASE + 0x3000UL) +#define TSC_BASE (AHB1PERIPH_BASE + 0x4000UL) + + +#define DMA1_Channel1_BASE (DMA1_BASE + 0x0008UL) +#define DMA1_Channel2_BASE (DMA1_BASE + 0x001CUL) +#define DMA1_Channel3_BASE (DMA1_BASE + 0x0030UL) +#define DMA1_Channel4_BASE (DMA1_BASE + 0x0044UL) +#define DMA1_Channel5_BASE (DMA1_BASE + 0x0058UL) +#define DMA1_Channel6_BASE (DMA1_BASE + 0x006CUL) +#define DMA1_Channel7_BASE (DMA1_BASE + 0x0080UL) +#define DMA1_CSELR_BASE (DMA1_BASE + 0x00A8UL) + + +#define DMA2_Channel1_BASE (DMA2_BASE + 0x0008UL) +#define DMA2_Channel2_BASE (DMA2_BASE + 0x001CUL) +#define DMA2_Channel3_BASE (DMA2_BASE + 0x0030UL) +#define DMA2_Channel4_BASE (DMA2_BASE + 0x0044UL) +#define DMA2_Channel5_BASE (DMA2_BASE + 0x0058UL) +#define DMA2_Channel6_BASE (DMA2_BASE + 0x006CUL) +#define DMA2_Channel7_BASE (DMA2_BASE + 0x0080UL) +#define DMA2_CSELR_BASE (DMA2_BASE + 0x00A8UL) + + +/*!< AHB2 peripherals */ +#define GPIOA_BASE (AHB2PERIPH_BASE + 0x0000UL) +#define GPIOB_BASE (AHB2PERIPH_BASE + 0x0400UL) +#define GPIOC_BASE (AHB2PERIPH_BASE + 0x0800UL) +#define GPIOD_BASE (AHB2PERIPH_BASE + 0x0C00UL) +#define GPIOE_BASE (AHB2PERIPH_BASE + 0x1000UL) +#define GPIOF_BASE (AHB2PERIPH_BASE + 0x1400UL) +#define GPIOG_BASE (AHB2PERIPH_BASE + 0x1800UL) +#define GPIOH_BASE (AHB2PERIPH_BASE + 0x1C00UL) + +#define USBOTG_BASE (AHB2PERIPH_BASE + 0x08000000UL) + +#define ADC1_BASE (AHB2PERIPH_BASE + 0x08040000UL) +#define ADC2_BASE (AHB2PERIPH_BASE + 0x08040100UL) +#define ADC3_BASE (AHB2PERIPH_BASE + 0x08040200UL) +#define ADC123_COMMON_BASE (AHB2PERIPH_BASE + 0x08040300UL) + + +#define RNG_BASE (AHB2PERIPH_BASE + 0x08060800UL) + + +/*!< FMC Banks registers base address */ +#define FMC_Bank1_R_BASE (FMC_R_BASE + 0x0000UL) +#define FMC_Bank1E_R_BASE (FMC_R_BASE + 0x0104UL) +#define FMC_Bank3_R_BASE (FMC_R_BASE + 0x0080UL) + +/* Debug MCU registers base address */ +#define DBGMCU_BASE (0xE0042000UL) + +/*!< USB registers base address */ +#define USB_OTG_FS_PERIPH_BASE (0x50000000UL) + +#define USB_OTG_GLOBAL_BASE (0x00000000UL) +#define USB_OTG_DEVICE_BASE (0x00000800UL) +#define USB_OTG_IN_ENDPOINT_BASE (0x00000900UL) +#define USB_OTG_OUT_ENDPOINT_BASE (0x00000B00UL) +#define USB_OTG_EP_REG_SIZE (0x00000020UL) +#define USB_OTG_HOST_BASE (0x00000400UL) +#define USB_OTG_HOST_PORT_BASE (0x00000440UL) +#define USB_OTG_HOST_CHANNEL_BASE (0x00000500UL) +#define USB_OTG_HOST_CHANNEL_SIZE (0x00000020UL) +#define USB_OTG_PCGCCTL_BASE (0x00000E00UL) +#define USB_OTG_FIFO_BASE (0x00001000UL) +#define USB_OTG_FIFO_SIZE (0x00001000UL) + +#define QSPI_BASE (0x90000000UL) /*!< QUADSPI memories accessible over AHB base address */ +#define QSPI_R_BASE (0xA0001000UL) /*!< QUADSPI control registers base address */ + +enum { + DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */ + DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */ + DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */ + DMA2_Channel4_IRQn = 59, /*!< DMA2 Channel 4 global Interrupt */ + DMA2_Channel5_IRQn = 60, /*!< DMA2 Channel 5 global Interrupt */ + + LPTIM1_IRQn = 65, /*!< LP TIM1 interrupt */ + + DMA2_Channel6_IRQn = 68, /*!< DMA2 Channel 6 global interrupt */ + DMA2_Channel7_IRQn = 69, /*!< DMA2 Channel 7 global interrupt */ +}; + + +static inline void create_unimplemented_layer(const char *name, hwaddr base, hwaddr size) { + DeviceState *dev = qdev_create(NULL, TYPE_UNIMPLEMENTED_DEVICE); + + qdev_prop_set_string(dev, "name", name); + qdev_prop_set_uint64(dev, "size", size); + qdev_init_nofail(dev); + + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev), 0, base, -900); +} + +static void stm32l467_soc_initfn(Object *obj) { + STM32L467State *s = STM32L467_SOC(obj); + + sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), + TYPE_ARMV7M); + + sysbus_init_child_obj(obj, "pwr", &s->pwr, sizeof(s->pwr), + TYPE_STM32L476_PWR); + + sysbus_init_child_obj(obj, "lptim1", &s->lptim1, sizeof(s->lptim1), + TYPE_STM32L476_LPTIM); + + sysbus_init_child_obj(obj, "rcc", &s->rcc, sizeof(s->rcc), + TYPE_STM32L476_RCC); + + sysbus_init_child_obj(obj, "flash", &s->flash_r, sizeof(s->flash_r), + TYPE_STM32L476_FLASH); + + sysbus_init_child_obj(obj, "DMA2", &s->dma, sizeof(s->dma), + TYPE_STM32L476_DMA); + + sysbus_init_child_obj(obj, "QSPI", &s->qspi, sizeof(s->qspi), + TYPE_STM32L476_QSPI); + + for (int i = 0; i < 3; i++) { + sysbus_init_child_obj(obj, "I2C[*]", &s->i2c[i], sizeof(s->i2c[i]), TYPE_STM32L476_I2C); + char *name = g_strdup_printf("I2C%d", i+1); + qdev_prop_set_string(DEVICE(&s->i2c[i]), "name", name); + } + + sysbus_init_child_obj(obj, "SPI3", &s->spi[2], sizeof(s->spi[2]), + TYPE_STM32L476_SPI); + s->spi[2].rxdrq = s->dma.channels[0].drq[0b0011]; // RX + s->spi[2].txdrq = s->dma.channels[1].drq[0b0011]; // TX +} + +static void stm32l467_soc_realize(DeviceState *dev_soc, Error **errp) { + STM32L467State *s = STM32L467_SOC(dev_soc); + Error *err = NULL; + SysBusDevice *busdev; + + MemoryRegion *system_memory = get_system_memory(); + + create_unimplemented_layer("IO", 0, 0xFFFFFFFF); + + create_unimplemented_layer("TIM2", TIM2_BASE, 0x400); + create_unimplemented_layer("TIM3", TIM3_BASE, 0x400); + create_unimplemented_layer("RTC", RTC_BASE, 0x400); + create_unimplemented_layer("RNG", RNG_BASE, 0x400); + create_unimplemented_layer("USART1", USART1_BASE, 0x400); + create_unimplemented_layer("DMA1", DMA1_BASE, 0x400); + create_unimplemented_layer("IWDG", IWDG_BASE, 0x400); + create_unimplemented_layer("ADC1", ADC1_BASE, 0x100); + create_unimplemented_layer("ADC2", ADC2_BASE, 0x100); + create_unimplemented_layer("ADC3", ADC3_BASE, 0x100); + create_unimplemented_layer("ADC123_COMMON", ADC123_COMMON_BASE, 0x100); + create_unimplemented_layer("SPI1", SPI1_BASE, 0x400); + + create_unimplemented_layer("CRC", CRC_BASE, 0x400); + create_unimplemented_layer("TIM1", TIM1_BASE, 0x400); + + s->syscfg = qdev_create(NULL, "stm32l476-syscfg"); + qdev_init_nofail(s->syscfg); + sysbus_mmio_map(SYS_BUS_DEVICE(s->syscfg), 0, SYSCFG_BASE); + + /* GPIO */ + struct + { + hwaddr addr; + char *name; + uint32_t GPIOx_MODER; + uint32_t GPIOx_OSPEEDR; + uint32_t GPIOx_PUPDR; + } const gpio_desc[] = { + {GPIOA_BASE, "GPIOA", 0xABFFFFFF, 0x0C000000, 0x64000000}, + {GPIOB_BASE, "GPIOB", 0xFFFFFEBF, 0x00000000, 0x00000100}, + {GPIOC_BASE, "GPIOC", 0xFFFFFFFF, 0x00000000, 0x00000000}, + {GPIOD_BASE, "GPIOD", 0xFFFFFFFF, 0x00000000, 0x00000000}, + {GPIOE_BASE, "GPIOE", 0xFFFFFFFF, 0x00000000, 0x00000000}, + {GPIOF_BASE, "GPIOF", 0xFFFFFFFF, 0x00000000, 0x00000000}, + {GPIOG_BASE, "GPIOG", 0xFFFFFFFF, 0x00000000, 0x00000000}, + {GPIOH_BASE, "GPIOH", 0x0000000F, 0x00000000, 0x00000000}, + }; + for (int i = 0; i < ARRAY_SIZE(gpio_desc); ++i) + { + s->gpio[i] = qdev_create(NULL, TYPE_STM32L476_GPIO); + s->gpio[i]->id = gpio_desc[i].name; + qdev_init_nofail(s->gpio[i]); + sysbus_mmio_map(SYS_BUS_DEVICE(s->gpio[i]), 0, gpio_desc[i].addr); + } + + create_unimplemented_layer("EXTI", EXTI_BASE, 0x400); + create_unimplemented_layer("QUADSPI", QSPI_R_BASE, 0x400); + + memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32L467.flash", + FLASH_SIZE, &error_fatal); + + memory_region_init_alias(&s->flash_alias, + OBJECT(dev_soc), + "alias", + &s->flash, + 0, + FLASH_SIZE); + memory_region_add_subregion(system_memory, 0, &s->flash); + memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash_alias); + + memory_region_init_ram(&s->sram1, OBJECT(dev_soc), "STM32F205.sram1", 96 * 1024, &error_fatal); + memory_region_add_subregion(system_memory, 0x20000000, &s->sram1); // 0x18000 + // memsave 0x20000000 0x18000 ../sram1.bin + + memory_region_init_ram(&s->sram2, OBJECT(dev_soc), "STM32F205.sram2", 32 * 1024, &error_fatal); + memory_region_add_subregion(system_memory, 0x10000000, &s->sram2); // 0x8000 + // memsave 0x10000000 0x8000 ../sram2.bin + + DeviceState *armv7m = DEVICE(&s->armv7m); + qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); + qdev_prop_set_uint32(armv7m, "num-irq", 82); + object_property_set_link(OBJECT(&s->armv7m), OBJECT(system_memory), + "memory", &error_abort); + object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + /* PWR registers */ + object_property_set_bool(OBJECT(&s->pwr), true, "realized", &err); + busdev = SYS_BUS_DEVICE(&s->pwr); + sysbus_mmio_map(busdev, 0, PWR_BASE); + + /* LPTIM1 registers */ + object_property_set_bool(OBJECT(&s->lptim1), true, "realized", &err); + busdev = SYS_BUS_DEVICE(&s->lptim1); + sysbus_mmio_map(busdev, 0, LPTIM1_BASE); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, LPTIM1_IRQn)); + + /* RCC registers */ + object_property_set_bool(OBJECT(&s->rcc), true, "realized", &err); + busdev = SYS_BUS_DEVICE(&s->rcc); + sysbus_mmio_map(busdev, 0, RCC_BASE); + + /* FLASH registers */ + object_property_set_bool(OBJECT(&s->flash_r), true, "realized", &err); + busdev = SYS_BUS_DEVICE(&s->flash_r); + sysbus_mmio_map(busdev, 0, FLASH_R_BASE); + + /* DMA registers */ + object_property_set_bool(OBJECT(&s->dma), true, "realized", &err); + busdev = SYS_BUS_DEVICE(&s->dma); + sysbus_mmio_map(busdev, 0, DMA2_BASE); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, DMA2_Channel1_IRQn)); + sysbus_connect_irq(busdev, 1, qdev_get_gpio_in(armv7m, DMA2_Channel2_IRQn)); + sysbus_connect_irq(busdev, 2, qdev_get_gpio_in(armv7m, DMA2_Channel3_IRQn)); + sysbus_connect_irq(busdev, 3, qdev_get_gpio_in(armv7m, DMA2_Channel4_IRQn)); + sysbus_connect_irq(busdev, 4, qdev_get_gpio_in(armv7m, DMA2_Channel5_IRQn)); + sysbus_connect_irq(busdev, 5, qdev_get_gpio_in(armv7m, DMA2_Channel6_IRQn)); + sysbus_connect_irq(busdev, 6, qdev_get_gpio_in(armv7m, DMA2_Channel7_IRQn)); + + /* SPI registers */ + object_property_set_bool(OBJECT(&s->spi[2]), true, "realized", &err); + busdev = SYS_BUS_DEVICE(&s->spi[2]); + sysbus_mmio_map(busdev, 0, SPI3_BASE); + + /* QSPI registers */ + object_property_set_bool(OBJECT(&s->qspi), true, "realized", &err); + busdev = SYS_BUS_DEVICE(&s->qspi); + sysbus_mmio_map(busdev, 0, QSPI_BASE); + sysbus_mmio_map(busdev, 1, QSPI_R_BASE); + + /* I2C registers */ + for (int i = 0; i < 3; i++) { + object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", &err); + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[0]), 0, I2C1_BASE); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[1]), 0, I2C2_BASE); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[2]), 0, I2C3_BASE); + + system_clock_scale = 1000; +} + +static Property stm32l467_soc_properties[] = { + DEFINE_PROP_STRING("cpu-type", STM32L467State, cpu_type), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32l467_soc_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = stm32l467_soc_realize; + device_class_set_props(dc, stm32l467_soc_properties); +} + +static const TypeInfo stm32l467_soc_info = { + .name = TYPE_STM32L467_SOC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L467State), + .instance_init = stm32l467_soc_initfn, + .class_init = stm32l467_soc_class_init, +}; + +static void stm32l467_soc_types(void) { + type_register_static(&stm32l467_soc_info); +} + +type_init(stm32l467_soc_types) \ No newline at end of file diff --git a/hw/arm/stm32l476_dma.c b/hw/arm/stm32l476_dma.c new file mode 100644 index 0000000000000..a732091378da7 --- /dev/null +++ b/hw/arm/stm32l476_dma.c @@ -0,0 +1,265 @@ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" +#include "hw/irq.h" + +#include "hw/arm/stm32l476_dma.h" + +#define DMA_ISR 0x00 +#define DMA_IFCR 0x04 +#define DMA_CCRx 0x08 + +#define DMA_CCR2 0x1C +#define DMA_CNDTR2 0x20 +#define DMA_CPAR2 0x24 +#define DMA_CMAR2 0x28 + +#define DMA_CSELR 0xA8 + +static void stm32l476_dma_reset(DeviceState *dev) { + STM32L476DmaState *s = STM32L476_DMA(dev); +} + +static void update_interrupt(STM32L476DmaState *s) { + for (int c = 0; c < 7; c++) { + int state = 0; + if (s->channels[c].HTIE && s->channels[c].HTIF) + state = 1; + if (s->channels[c].TCIE && s->channels[c].TCIF) + state = 1; + if (s->channels[c].TEIE && s->channels[c].TEIF) + state = 1; + + qemu_set_irq(s->irq[c], state); + } + +} + +static void enable_channel(STM32L476DmaState *s, int c, bool enabled) { + STM32L476DmaChannelState *channel = &s->channels[c]; + if (enabled == channel->EN) + return; + + channel->EN = enabled; + +} + +static uint64_t stm32l476_dma_read(void *opaque, hwaddr offset, + unsigned int size) { + STM32L476DmaState *s = opaque; + + switch (offset) { + case DMA_ISR: { + uint32_t out = 0; + for (int i = 0; i < 7; i++) { + out |= (s->channels[i].TCIF || s->channels[i].HTIF || s->channels[i].TEIF) << (0 + 4 * i); + out |= s->channels[i].TCIF << (1 + 4 * i); + out |= s->channels[i].HTIF << (2 + 4 * i); + out |= s->channels[i].TEIF << (3 + 4 * i); + } + return out; + } + + case DMA_CCR2: { + STM32L476DmaChannelState *channel = &s->channels[1]; + uint32_t out = 0; + out |= channel->EN << 0; + out |= channel->TCIE << 1; + out |= channel->HTIE << 2; + out |= channel->TEIE << 3; + out |= channel->DIR << 4; + out |= channel->CIRC << 5; + out |= channel->PINC << 6; + out |= channel->MINC << 7; + out |= channel->PSIZE << 8; + out |= channel->MSIZE << 10; + out |= channel->PL << 12; + out |= channel->MEM2MEM << 14; + return out; + } + + case DMA_CSELR: { + uint32_t out = 0; + out |= s->channels[0].CxS << 0; + out |= s->channels[1].CxS << 4; + out |= s->channels[2].CxS << 8; + out |= s->channels[3].CxS << 12; + out |= s->channels[4].CxS << 16; + out |= s->channels[5].CxS << 20; + out |= s->channels[6].CxS << 24; + return out; + } + + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + return 0; +} + +static void stm32l476_dma_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned int size) { + STM32L476DmaState *s = opaque; + + if (offset >= DMA_CCRx) { + hwaddr pos = offset - DMA_CCRx; + int channel = pos / 0x14; + int reg = pos % 0x14; + + } + + switch (offset) { + case DMA_IFCR: + for (int c = 0; c < 7; c++) { + if (extract64(val64, c * 4 + 0, 1) == 1) { + // CGIF1 + s->channels[c].TCIF = false; + s->channels[c].HTIF = false; + s->channels[c].TEIF = false; + } + if (extract64(val64, c * 4 + 1, 1) == 1) { + // CTCIF1 + s->channels[c].TCIF = false; + } + if (extract64(val64, c * 4 + 2, 1) == 1) { + // CHTIF1 + s->channels[c].HTIF = false; + } + if (extract64(val64, c * 4 + 3, 1) == 1) { + // CTEIF1 + s->channels[c].TEIF = false; + } + } + update_interrupt(s); + break; + + case DMA_CCR2: + enable_channel(s, 1, extract64(val64, 0, 1)); + + // not read-only, should warn + s->channels[1].TCIE = extract64(val64, 1, 1); + s->channels[1].HTIE = extract64(val64, 2, 1); + s->channels[1].TEIE = extract64(val64, 3, 1); + s->channels[1].CIRC = extract64(val64, 5, 1); + + // read only, should probably error on difference + if (s->channels[1].EN == false) { + s->channels[1].DIR = extract64(val64, 4, 1); + s->channels[1].PINC = extract64(val64, 6, 1); + s->channels[1].MINC = extract64(val64, 7, 1); + s->channels[1].PSIZE = extract64(val64, 8, 2); + s->channels[1].MSIZE = extract64(val64, 10, 2); + s->channels[1].PL = extract64(val64, 12, 2); + s->channels[1].MEM2MEM = extract64(val64, 14, 1); + } + break; + case DMA_CNDTR2: + // read only, should probably error on difference + if (s->channels[1].EN == false) { + s->channels[1].NDTR = extract64(val64, 0, 16); + } + break; + case DMA_CPAR2: + // not read-only, should warn + s->channels[1].PA = extract64(val64, 0, 32); + break; + case DMA_CMAR2: + // not read-only, should warn + s->channels[1].MA = extract64(val64, 0, 32); + break; + + case DMA_CSELR: + s->channels[0].CxS = extract64(val64, 0, 4); + s->channels[1].CxS = extract64(val64, 4, 4); + s->channels[2].CxS = extract64(val64, 8, 4); + s->channels[3].CxS = extract64(val64, 12, 4); + s->channels[4].CxS = extract64(val64, 16, 4); + s->channels[5].CxS = extract64(val64, 20, 4); + s->channels[6].CxS = extract64(val64, 24, 4); + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, val64, offset); + break; + } +} + +static const MemoryRegionOps stm32l476_dma_ops = { + .read = stm32l476_dma_read, + .write = stm32l476_dma_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void omap_dma_request(void *opaque, int drq, int req) { + if (req == 0) + return; + + STM32L476DmaChannelState *channel = opaque; + if (channel->CxS != drq) + return; + + assert(channel->EN); + + assert(channel->DIR == 1); + assert(channel->PSIZE == channel->MSIZE); + assert(channel->PSIZE == 0); + assert(channel->PINC == false); + assert(channel->MINC == true); + + uint8_t *buffer = malloc(channel->NDTR); + cpu_physical_memory_read(channel->MA, buffer, channel->NDTR); + for (int i = 0; i < channel->NDTR; i++) { + cpu_physical_memory_write(channel->PA, &buffer[i], 1); + } + + free(buffer); + + channel->TCIF = true; + update_interrupt(channel->parent); +} + +static void stm32l476_dma_init(Object *obj) { + STM32L476DmaState *s = STM32L476_DMA(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l476_dma_ops, s, + TYPE_STM32L476_DMA, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + for (int i = 0; i < 7; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq[i]); + } + + for (int c = 0; c < 7; c++) { + s->channels[c].parent = s; + s->channels[c].id = c; + s->channels[c].drq = qemu_allocate_irqs(omap_dma_request, &s->channels[c], 8); + } +} + +static void stm32l476_dma_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_dma_reset; +} + +static const TypeInfo stm32l476_dma_info = { + .name = TYPE_STM32L476_DMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476DmaState), + .instance_init = stm32l476_dma_init, + .class_init = stm32l476_dma_class_init, +}; + +static void stm32l476_dma_register_types(void) { + type_register_static(&stm32l476_dma_info); +} + +type_init(stm32l476_dma_register_types) diff --git a/hw/arm/stm32l476_exti.c b/hw/arm/stm32l476_exti.c new file mode 100644 index 0000000000000..7715e9b2b7653 --- /dev/null +++ b/hw/arm/stm32l476_exti.c @@ -0,0 +1,74 @@ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" + +#include "hw/arm/stm32l476_rcc.h" + +static void stm32l476_rcc_reset(DeviceState *dev) { + STM32L476RccState *s = STM32L476_RCC(dev); +} + +static uint64_t stm32l476_rcc_read(void *opaque, hwaddr offset, + unsigned int size) { + STM32L476RccState *s = opaque; + + switch (offset) { + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + return 0; +} + +static void stm32l476_rcc_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned int size) { + STM32L476RccState *s = opaque; + + switch (offset) { + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, val64, offset); + break; + } +} + +static const MemoryRegionOps stm32l476_rcc_ops = { + .read = stm32l476_rcc_read, + .write = stm32l476_rcc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32l476_rcc_init(Object *obj) { + STM32L476RccState *s = STM32L476_RCC(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l476_rcc_ops, s, + TYPE_STM32L476_RCC, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void stm32l476_rcc_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_rcc_reset; +} + +static const TypeInfo stm32l476_rcc_info = { + .name = TYPE_STM32L476_RCC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476RccState), + .instance_init = stm32l476_rcc_init, + .class_init = stm32l476_rcc_class_init, +}; + +static void stm32l476_rcc_register_types(void) { + type_register_static(&stm32l476_rcc_info); +} + +type_init(stm32l476_rcc_register_types) diff --git a/hw/arm/stm32l476_flash.c b/hw/arm/stm32l476_flash.c new file mode 100644 index 0000000000000..a04f3309b8893 --- /dev/null +++ b/hw/arm/stm32l476_flash.c @@ -0,0 +1,101 @@ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" + +#include "hw/arm/stm32l476_flash.h" + +#define ACR 0x00 +#define PDKEYR 0x04 +#define KEYR 0x08 +#define OPTKEYR 0x0C +#define SR 0x10 +#define CR 0x14 +#define ECCR 0x18 +#define RESERVED1 0x1C +#define OPTR 0x20 +#define PCROP1SR 0x24 +#define PCROP1ER 0x28 +#define WRP1AR 0x2C +#define WRP1BR 0x30 +#define PCROP2SR 0x44 +#define PCROP2ER 0x48 +#define WRP2AR 0x4C +#define WRP2BR 0x50 + +static void stm32l476_flash_reset(DeviceState *dev) { + STM32L476FlashState *s = STM32L476_FLASH(dev); + s->acr_latency = 0; +} + +static uint64_t stm32l476_flash_read(void *opaque, hwaddr offset, + unsigned int size) { + STM32L476FlashState *s = opaque; + + switch (offset) { + case ACR: + return s->acr_latency; + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + return 0; +} + +static void stm32l476_flash_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned int size) { + STM32L476FlashState *s = opaque; + + switch (offset) { + case ACR: + s->acr_latency = val64 & 0x7; + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, val64, offset); + break; + } +} + +static const MemoryRegionOps stm32l476_flash_ops = { + .read = stm32l476_flash_read, + .write = stm32l476_flash_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32l476_flash_init(Object *obj) { + STM32L476FlashState *s = STM32L476_FLASH(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l476_flash_ops, s, + TYPE_STM32L476_FLASH, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void stm32l476_flash_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_flash_reset; +} + +static const TypeInfo stm32l476_flash_info = { + .name = TYPE_STM32L476_FLASH, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476FlashState), + .instance_init = stm32l476_flash_init, + .class_init = stm32l476_flash_class_init, +}; + +static void stm32l476_flash_register_types(void) { + type_register_static(&stm32l476_flash_info); +} + +type_init(stm32l476_flash_register_types) diff --git a/hw/arm/stm32l476_gpio.c b/hw/arm/stm32l476_gpio.c new file mode 100644 index 0000000000000..1c0c1a2faee6d --- /dev/null +++ b/hw/arm/stm32l476_gpio.c @@ -0,0 +1,319 @@ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" +#include "hw/arm/stm32l476_gpio.h" + +typedef struct +{ + uint8_t mode; + uint64_t AFSEL; + uint64_t OT; + uint64_t PUPD; + uint64_t OSPEED; +} GpioPin; + +static const int GPIOx_MODER = 0x00; +static const int GPIOx_OTYPER = 0x04; +static const int GPIOx_OSPEEDR = 0x08; +static const int GPIOx_PUPDR = 0x0C; +static const int GPIOx_IDR = 0x10; +static const int GPIOx_BSRR = 0x18; +static const int GPIOx_AFRH = 0x24; +static const int GPIOx_BRR = 0x28; +static const int GPIOx_ASCR = 0x2c; + +struct __STM32L476GpioState +{ + /* */ + SysBusDevice parent_obj; + + GpioPin pins[16]; + + /* */ + MemoryRegion mmio; + qemu_irq irq; +}; + +static void +stm32l476_gpio_reset(DeviceState *dev) +{ + STM32L476GpioState *s = STM32L476_GPIO(dev); +} + +static uint64_t +stm32l476_gpio_read(void *opaque, hwaddr offset, + unsigned int size) +{ + STM32L476GpioState *s = opaque; + + assert(size == 4); + switch (offset) + { + case GPIOx_MODER: + { + uint32_t out = 0; + + for (int i = 0; i < 16; i++) + { + out |= s->pins[i].mode << (i * 2); + } + + return out; + } + + case GPIOx_OTYPER: + { + uint32_t out = 0; + + for (int i = 0; i < 16; i++) + { + out |= s->pins[i].OT << i; + } + + return out; + } + + case GPIOx_OSPEEDR: + { + uint32_t out = 0; + + for (int i = 0; i < 16; i++) + { + out |= s->pins[i].OSPEED << (i * 2); + } + + return out; + } + + case GPIOx_PUPDR: + { + uint32_t out = 0; + + for (int i = 0; i < 16; i++) + { + out |= s->pins[i].PUPD << (i * 2); + } + + return out; + } + + case GPIOx_AFRL: + { + uint32_t out = 0; + + for (int i = 0; i < 8; i++) + { + out |= s->pins[i].AFSEL << (i * 4); + } + + return out; + } + + case GPIOx_AFRH: + { + uint32_t out = 0; + + for (int i = 0; i < 8; i++) + { + out |= s->pins[8 + i].AFSEL << (i * 4); + } + + return out; + } + + case GPIOx_IDR: + { + return 0x20; + } + + case GPIOx_ASCR: + default: + { + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + } + return 0; +} + +static void +stm32l476_gpio_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned int size) +{ + STM32L476GpioState *s = opaque; + + GpioPin backup[16] = {0}; + memcpy(backup, s->pins, sizeof(s->pins)); + + assert(size == 4); + switch (offset) + { + case GPIOx_MODER: + { + for (int i = 0; i < 16; i++) + { + s->pins[i].mode = extract64(val64, i * 2, 2); + } + break; + } + + case GPIOx_OTYPER: + { + for (int i = 0; i < 16; i++) + { + s->pins[i].OT = extract64(val64, i, 1); + } + break; + } + + case GPIOx_OSPEEDR: + { + for (int i = 0; i < 16; i++) + { + s->pins[i].OSPEED = extract64(val64, i * 2, 2); + } + break; + } + + case GPIOx_PUPDR: + { + for (int i = 0; i < 16; i++) + { + s->pins[i].PUPD = extract64(val64, i * 2, 2); + } + break; + } + + case GPIOx_BSRR: + { + // If there is an attempt to both set and reset a bit in GPIOx_BSRR, the set action takes priority. + for (int i = 0; i < 16; i++) + { + if (extract64(val64, i, 1)) + { + //set + } + + if (extract64(val64, 16 + i, 1)) + { + //reset + } + } + break; + } + + case GPIOx_AFRL: + { + for (int i = 0; i < 8; i++) + { + s->pins[i].AFSEL = extract64(val64, i * 4, 4); + } + break; + } + + case GPIOx_AFRH: + { + for (int i = 0; i < 8; i++) + { + s->pins[i + 8].AFSEL = extract64(val64, i * 4, 4); + } + break; + } + + case GPIOx_BRR: + { + for (int i = 0; i < 16; i++) + { + if (extract64(val64, i, 1)) + { + // Reset the corresponding ODx bit + } + } + break; + } + + case GPIOx_ASCR: + { + for (int i = 0; i < 16; i++) + { + if (extract64(val64, i, 1)) + { + // Connect analog switch to the ADC input + } + } + break; + }; + + default:qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, val64, offset); + break; + } + + for (int i = 0; i < 16; i++) + { + GpioPin *x; + if (memcmp(&backup[i], &s->pins[i], sizeof(s->pins[0])) == 0) + continue; + + printf("[%s] Pin %d changed to ", DEVICE(s)->id, i); + +// x = &backup[i]; +// printf("[mode:%d AFSEL:%d ospeed:%d ot:%d pupd:%d]", x->mode, x->AFSEL, x->OSPEED, x->OT, x->PUPD); +// printf(" to "); + x = &s->pins[i]; + printf("[mode:%d ", x->mode); + if (x->mode == 0b10) + { + printf("AFSEL:%d ", x->AFSEL); + } + printf("ospeed:%d ot:%d pupd:%d]", x->OSPEED, x->OT, x->PUPD); + printf("\n"); + } +} + +static const MemoryRegionOps stm32l476_gpio_ops = { + .read = stm32l476_gpio_read, + .write = stm32l476_gpio_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void +stm32l476_gpio_init(Object *obj) +{ + STM32L476GpioState *s = STM32L476_GPIO(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l476_gpio_ops, s, + TYPE_STM32L476_GPIO, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void +stm32l476_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_gpio_reset; +} + +static const TypeInfo stm32l476_gpio_info = { + .name = TYPE_STM32L476_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476GpioState), + .instance_init = stm32l476_gpio_init, + .class_init = stm32l476_gpio_class_init, +}; + +static void +stm32l476_gpio_register_types(void) +{ + type_register_static(&stm32l476_gpio_info); +} + +type_init(stm32l476_gpio_register_types) diff --git a/hw/arm/stm32l476_i2c.c b/hw/arm/stm32l476_i2c.c new file mode 100644 index 0000000000000..3393e3642a762 --- /dev/null +++ b/hw/arm/stm32l476_i2c.c @@ -0,0 +1,184 @@ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" +#include "hw/i2c/i2c.h" + +#include "hw/arm/stm32l476_i2c.h" +#include "hw/qdev-properties.h" + +#include "qapi/error.h" + +#define I2C_CR1 0x00 +#define I2C_CR2 0x04 +#define I2C_OAR1 0x08 +#define I2C_OAR2 0x0C +#define I2C_TIMINGR 0x10 +#define I2C_ISR 0x18 +#define I2C_TXDR 0x28 + +static void +stm32l476_i2c_reset(DeviceState *dev) +{ + STM32L476I2CState *s = STM32L476_I2C(dev); +} + +static uint64_t +stm32l476_i2c_read(void *opaque, hwaddr offset, + unsigned int size) +{ + STM32L476I2CState *s = opaque; + + switch (offset) + { + case I2C_CR2: + { + uint32_t out = 0; + out |= s->SADD << 0; + out |= s->RD_WRN << 10; + out |= s->ADD10 << 11; + out |= s->HEAD10R << 12; + out |= s->START << 13; + out |= s->STOP << 14; + out |= s->NACK << 15; + + out |= s->NBYTES << 16; + out |= s->RELOAD << 24; + out |= s->AUTOEND << 25; + out |= s->PECBYTE << 26; + return out; + } + + break; +// case I2C_CR1: +// case I2C_OAR1: +// case I2C_OAR2: + case I2C_ISR: + { + uint32_t out = 0; + out |= s->TXIS << 1; + out |= s->BUSY << 15; + return out; + } +// break; + default:qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + return 0; +} + +static void +stm32l476_i2c_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned int size) +{ + STM32L476I2CState *s = opaque; + assert(size == 4); + + switch (offset) + { + case I2C_CR2: + { + s->SADD = extract64(val64, 0, 10); + s->RD_WRN = extract64(val64, 10, 1); + s->ADD10 = extract64(val64, 11, 1); + s->HEAD10R = extract64(val64, 12, 1); + + if (extract64(val64, 13, 1) == true) + s->START = true; + if (extract64(val64, 14, 1) == true) + s->STOP = true; + if (extract64(val64, 15, 1) == true) + s->NACK = true; + + s->NBYTES = extract64(val64, 16, 8); + s->RELOAD = extract64(val64, 24, 1); + s->AUTOEND = extract64(val64, 25, 1); + + if (extract64(val64, 26, 1) == true) + s->PECBYTE = true; + + if (s->START && !s->BUSY) + { + s->BUSY = true; + i2c_start_transfer(s->bus, (s->SADD & 0b0011111110) >> 1, s->RD_WRN == 1); + } + s->TXIS = true; + break; + } + + case I2C_TXDR: + { + printf("[%s] Send data to 0x%X: 0x%02X\n", s->name, (s->SADD & 0b0011111110) >> 1, val64); + i2c_send(s->bus, val64 & 0xFF); + break; + } + + case I2C_CR1: + case I2C_OAR1: + case I2C_OAR2: + case I2C_TIMINGR:break; + default:qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, val64, offset); + break; + } +} + +static const MemoryRegionOps stm32l476_i2c_ops = { + .read = stm32l476_i2c_read, + .write = stm32l476_i2c_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void +stm32l476_i2c_realize(DeviceState *dev, Error **errp) +{ + STM32L476I2CState *s = STM32L476_I2C(dev); + + if (s->name == NULL) + { + error_setg(errp, "property 'name' not specified"); + return; + } + + memory_region_init_io(&s->mmio, OBJECT(s), &stm32l476_i2c_ops, s, + TYPE_STM32L476_I2C, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); + + s->bus = i2c_init_bus(dev, "i2c"); +} + +static Property stm32l476_i2c_properties[] = { + DEFINE_PROP_STRING("name", STM32L476I2CState, name), + DEFINE_PROP_END_OF_LIST(), +}; + +static void +stm32l476_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_i2c_reset; + dc->realize = stm32l476_i2c_realize; + device_class_set_props(dc, stm32l476_i2c_properties); +} + +static const TypeInfo stm32l476_i2c_info = { + .name = TYPE_STM32L476_I2C, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476I2CState), + .class_init = stm32l476_i2c_class_init, +}; + +static void +stm32l476_i2c_register_types(void) +{ + type_register_static(&stm32l476_i2c_info); +} + +type_init(stm32l476_i2c_register_types) diff --git a/hw/arm/stm32l476_lptim.c b/hw/arm/stm32l476_lptim.c new file mode 100644 index 0000000000000..cdcea7f9dceff --- /dev/null +++ b/hw/arm/stm32l476_lptim.c @@ -0,0 +1,254 @@ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" +#include "hw/irq.h" + +#include "hw/arm/stm32l476_lptim.h" + + +// NOTE: The usleep() helps the MacOS stdout from freezing when we have a lot of print out +#define DPRINTF(fmt, ...) \ + do { \ + qemu_log_mask(LOG_GUEST_ERROR,"LPTIM %s: " fmt , __func__, ## __VA_ARGS__); \ + usleep(100); \ + } while (0) + +#define ISR 0x00 +#define LPTIM_ICR 0x04 +#define LPTIM_IER 0x08 +#define LPTIM_CFGR 0x0C +#define LPTIM_CR 0x10 +#define CMP 0x14 +#define LPTIM_ARR 0x18 +#define LPTIM_CNT 0x1C +#define LPTIM_OR 0x20 + +#define TIMER_NS (1000000000/32768) + +static int64_t stm32l47xx_lptim_next_transition(STM32L476LPTimState *s, int64_t current_time) { + return current_time + TIMER_NS * s->ARR ; +} + +static void stm32l476_lptim_interrupt(void *opaque) { +// STM32L476LPTimState *s = opaque; +// +// int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +// s->start = now; +// timer_mod(s->timer, stm32l47xx_lptim_next_transition(s, now)); +// +// if (s->ARRMIE && s->ARRM == false) { +// s->ARRM = true; +// qemu_set_irq(s->irq, 1); +// } + +} + +static void stm32l476_lptim_timer_cb(void *opaque) { + STM32L476LPTimState *s = opaque; + + if (s->ARRMIE && s->ARRM == false) { + s->ARRM = true; + qemu_irq_pulse(s->irq); + } +} + +static void stm32l476_lptim_reset(DeviceState *dev) { + STM32L476LPTimState *s = STM32L476_LPTIM(dev); + s->CNT = 0; +} + +static uint64_t stm32l476_lptim_read(void *opaque, hwaddr offset, + unsigned int size) { + STM32L476LPTimState *s = opaque; + + switch (offset) { + case ISR: { + uint32_t out = 0; + out |= s->CMPM << 0; + out |= s->ARRM << 1; + out |= s->EXTTRIG << 2; + out |= s->CMPOK << 3; + out |= s->ARROK << 4; + out |= s->UP << 5; + out |= s->DOWN << 6; + return out; + } + case LPTIM_CNT: { +// int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +// int64_t delta = now - s->start; +// +// int16_t ticks = delta / TIMER_NS; + ptimer_transaction_begin(s->ptimer); + uint64_t kI = ptimer_get_limit(s->ptimer) - ptimer_get_count(s->ptimer)-1; + ptimer_transaction_commit(s->ptimer); + return kI; + } + + case LPTIM_IER: { + uint32_t out = 0; + out |= s->CMPMIE << 0; + out |= s->ARRMIE << 1; + out |= s->EXTTRIGIE << 2; + out |= s->CMPOKIE << 3; + out |= s->ARROKIE << 4; + out |= s->UPIE << 5; + out |= s->DOWNIE << 6; + return out; + } + + case LPTIM_ICR: + case LPTIM_CFGR: + case LPTIM_ARR: + break; + + case LPTIM_CR: { + uint32_t out = 0; + out |= s->ENABLE << 0; + out |= s->SNGSTRT << 1; + out |= s->CNTSTRT << 2; + return out; + } + + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + return 0; +} + +static void stm32l476_lptim_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned int size) { + STM32L476LPTimState *s = opaque; + + switch (offset) { + case LPTIM_ICR: { + if ((val64 >> 0) & 1) + s->CMPM = false; + if ((val64 >> 1) & 1) + s->ARRM = false; + if ((val64 >> 2) & 1) + s->EXTTRIG = false; + if ((val64 >> 3) & 1) + s->CMPOK = false; + if ((val64 >> 4) & 1) + s->ARROK = false; + if ((val64 >> 5) & 1) + s->UP = false; + if ((val64 >> 6) & 1) + s->DOWN = false; + break; + } + + case LPTIM_CR: { + bool ENABLE = (val64 >> 0) & 1; + bool SNGSTRT = (val64 >> 1) & 1; + bool CNTSTRT = (val64 >> 2) & 1; + if (CNTSTRT == true && s->CNTSTRT == false) { + DPRINTF("started\n"); + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->start = now; +// timer_mod(s->timer, stm32l47xx_lptim_next_transition(s, now)); + + ptimer_transaction_begin(s->ptimer); + ptimer_set_limit(s->ptimer, s->ARR, true); + ptimer_run(s->ptimer, 0); + ptimer_transaction_commit(s->ptimer); + } else if (CNTSTRT == false && s->CNTSTRT == true) { + DPRINTF("stopped\n"); +// timer_del(s->timer); + ptimer_transaction_begin(s->ptimer); + ptimer_stop(s->ptimer); + ptimer_transaction_commit(s->ptimer); + } + + s->ENABLE = ENABLE; + s->SNGSTRT = SNGSTRT; + s->CNTSTRT = CNTSTRT; + DPRINTF("ENABLE:%d SNGSTRT:%d CNTSTRT:%d\n", ENABLE, SNGSTRT, CNTSTRT); + break; + } + + case LPTIM_IER: { + if (s->ENABLE) { + qemu_log_mask(LOG_GUEST_ERROR, + "The LPTIM_IER register must only be modified when the LPTIM is disabled"); + return; + } + s->CMPMIE = (val64 >> 0) & 1; + s->ARRMIE = (val64 >> 1) & 1; + s->EXTTRIGIE = (val64 >> 2) & 1; + s->CMPOKIE = (val64 >> 3) & 1; + s->ARROKIE = (val64 >> 4) & 1; + s->UPIE = (val64 >> 5) & 1; + s->DOWNIE = (val64 >> 6) & 1; + } + + case LPTIM_CFGR : + case LPTIM_OR: + break; + + case LPTIM_ARR: { + s->ARR = val64 & 0xFFFF; + s->ARROK = true; + break; + } + + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, val64, offset); + break; + } +} + +static const MemoryRegionOps stm32l476_lptim_ops = { + .read = stm32l476_lptim_read, + .write = stm32l476_lptim_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32l476_lptim_init(Object *obj) { + STM32L476LPTimState *s = STM32L476_LPTIM(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l476_lptim_ops, s, + TYPE_STM32L476_LPTIM, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); +} + + +static void stm32l476_lptim_realize(DeviceState *dev, Error **errp) { + STM32L476LPTimState *s = STM32L476_LPTIM(dev); +// s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, stm32l476_lptim_interrupt, s); + s->ptimer = ptimer_init(stm32l476_lptim_timer_cb, s, PTIMER_POLICY_DEFAULT); + ptimer_transaction_begin(s->ptimer); + ptimer_set_freq(s->ptimer, 32768); + ptimer_transaction_commit(s->ptimer); +} + +static void stm32l476_lptim_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_lptim_reset; + dc->realize = stm32l476_lptim_realize; +} + +static const TypeInfo stm32l476_lptim_info = { + .name = TYPE_STM32L476_LPTIM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476LPTimState), + .instance_init = stm32l476_lptim_init, + .class_init = stm32l476_lptim_class_init, +}; + +static void stm32l476_lptim_register_types(void) { + type_register_static(&stm32l476_lptim_info); +} + +type_init(stm32l476_lptim_register_types) diff --git a/hw/arm/stm32l476_pwr.c b/hw/arm/stm32l476_pwr.c new file mode 100644 index 0000000000000..62fa31cd4eabd --- /dev/null +++ b/hw/arm/stm32l476_pwr.c @@ -0,0 +1,123 @@ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" + +#include "hw/arm/stm32l476_pwr.h" + +#define CR1 0x00 +#define SR2 0x14 + +static void stm32l476_pwr_reset(DeviceState *dev) { + STM32L476PwrState *s = STM32L476_PWR(dev); + + // CR1 + s->LPMS = 0; + s->LPMS = 0; + s->LPMS = 0; + s->LPMS = 0; + + // SR2 + s->REGLPS = 0; + s->REGLPF = 0; + s->VOSF = 0; + s->PVDO = 0; + s->PVMO1 = 0; + s->PVMO2 = 0; + s->PVMO3 = 0; + s->PVMO4 = 0; +} + +static uint64_t stm32l476_pwr_read(void *opaque, hwaddr offset, + unsigned int size) { + STM32L476PwrState *s = opaque; + + switch (offset) { + case CR1: { + uint32_t out = 0; + + out |= s->LPMS << 0; + out |= s->DBP << 8; + out |= s->VOS << 9; + out |= s->LPR << 14; + return out; + } + case SR2: { + uint32_t out = 0; + + out |= s->REGLPS << 8; + out |= s->REGLPF << 9; + out |= s->VOSF << 10; + out |= s->PVDO << 11; + out |= s->PVMO1 << 12; + out |= s->PVMO2 << 13; + out |= s->PVMO3 << 14; + out |= s->PVMO4 << 15; + return out; + } + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + return 0; +} + +static void stm32l476_pwr_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned int size) { + STM32L476PwrState *s = opaque; + + switch (offset) { + case CR1: { + s->LPMS = (val64 >> 0) & 0b111; + s->DBP = (val64 >> 8) & 0b1; + // TODO: disallow writing 0b00/0b11 to VOS + s->VOS = (val64 >> 9) & 0b11; + s->LPR = (val64 >> 14) & 0b1; + break; + } + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, val64, offset); + break; + } +} + +static const MemoryRegionOps stm32l476_pwr_ops = { + .read = stm32l476_pwr_read, + .write = stm32l476_pwr_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32l476_pwr_init(Object *obj) { + STM32L476PwrState *s = STM32L476_PWR(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l476_pwr_ops, s, + TYPE_STM32L476_PWR, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void stm32l476_pwr_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_pwr_reset; +} + +static const TypeInfo stm32l476_pwr_info = { + .name = TYPE_STM32L476_PWR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476PwrState), + .instance_init = stm32l476_pwr_init, + .class_init = stm32l476_pwr_class_init, +}; + +static void stm32l476_pwr_register_types(void) { + type_register_static(&stm32l476_pwr_info); +} + +type_init(stm32l476_pwr_register_types) diff --git a/hw/arm/stm32l476_qspi.c b/hw/arm/stm32l476_qspi.c new file mode 100644 index 0000000000000..fb0c3a23debb8 --- /dev/null +++ b/hw/arm/stm32l476_qspi.c @@ -0,0 +1,513 @@ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" +#include "hw/irq.h" + +#include "hw/arm/stm32l476_qspi.h" + +#define QUADSPI_CR 0x00 +#define QUADSPI_DCR 0x04 +#define QUADSPI_SR 0x08 +#define QUADSPI_FCR 0x0C +#define QUADSPI_DLR 0x10 +#define QUADSPI_CCR 0x14 +#define QUADSPI_AR 0x18 +#define QUADSPI_DR 0x20 +#define QUADSPI_PSMKR 0x24 +#define QUADSPI_PSMAR 0x28 +#define QUADSPI_PIR 0x2C + +#ifndef DEBUG_QSPI +#define DEBUG_QSPI 0 +#endif + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_QSPI) { \ + qemu_log("qspi: " fmt , ## __VA_ARGS__); \ + } \ +} while (0) + +typedef enum +{ + Unknown, + + OnWriteCCR, + OnWriteAR, + OnWriteDR, +} StartMode; + +static StartMode +getStart(STM32L476QspiState *s) +{ + if (s->FMODE == 0b00 || s->FMODE == 0b01) + { + // Indirect mode + if (s->ADMODE == 0b00 && s->DMODE == 0b00) + return OnWriteCCR; + if (s->FMODE == 0b01 && s->ADMODE == 0b00) + return OnWriteCCR; + + if (s->ADMODE != 0b00) + { + if (s->FMODE == 0b01 || s->DMODE == 0b00) + return OnWriteAR; + } + + if (s->FMODE == 0b00 && s->DMODE != 0b00) + return OnWriteDR; + } + else if (s->FMODE == 0b10) + { + if (s->ADMODE == 0b00) + return OnWriteCCR; + + return OnWriteAR; + } + else + { + assert(false); + } + + return Unknown; +} + +static void +do_transfer(STM32L476QspiState *s) +{ + // Instruction phase + qemu_irq_lower(s->cs); + DPRINTF("Instruction: 0x%02X\n", s->INSTRUCTION); + ssi_transfer(s->qspi, s->INSTRUCTION); + + // Address phase + if (s->ADMODE != 0b00) + { + switch (s->ADSIZE) + { + case 0b10: + { + DPRINTF("Address: 0x%06X\n", s->ADDRESS & 0xFFFFFF); + ssi_transfer(s->qspi, (s->ADDRESS >> 16) & 0xFF); + ssi_transfer(s->qspi, (s->ADDRESS >> 8) & 0xFF); + ssi_transfer(s->qspi, (s->ADDRESS >> 0) & 0xFF); + break; + } + default:assert(false); + break; + } + } + + // Alternate-bytes phase + if (s->ABMODE != 0b00) + { + assert(false); + } + + // Dummy-cycles phase + if (s->DCYC != 0) + { + if(s->INSTRUCTION == 0xEB && s->DCYC == 6) { + + // Argument byte + uint8_t d = ssi_transfer(s->qspi, 0); + DPRINTF("- DUMMY: 0x%02X\n", d); + + // 4 cycles + for (int i = 0; i < 4; i++) { + uint8_t d = ssi_transfer(s->qspi, 0); + DPRINTF("- DUMMY: 0x%02X\n", d); + } + } else { + for (int i = 0; i < s->DCYC; i++) + { + uint8_t d = ssi_transfer(s->qspi, 0); + DPRINTF("- DUMMY: 0x%02X\n", d); + } + } + + } + + bool leaveCS = false; + + // Data phase + DPRINTF("Data:\n"); + if (s->DMODE != 0) + { + if (s->FMODE == 0b01) + { + for (int i = 0; i < s->DL + 1; i++) + { + uint8_t d = ssi_transfer(s->qspi, 0); + fifo8_push(&s->rx_fifo, d); + DPRINTF("- 0x%02X\n", d); + } + } + else if (s->FMODE == 0b00) + { + s->dataPushed = 0; + leaveCS = true; + } + else + { + assert(false); + } + } + + if (!leaveCS) + { + qemu_irq_raise(s->cs); + } + +} + +static void +start(STM32L476QspiState *s) +{ + + if (s->FMODE == 0b01 || s->FMODE == 0b00) + { + s->BUSY = true; + // QUADSPI indirect mode + do_transfer(s); + s->BUSY = false; + if (s->FMODE == 0b01 && !fifo8_is_empty(&s->rx_fifo)) + { + s->BUSY = true; + } + s->TCF = true; + } + else if (s->FMODE == 0b10) + { + // QUADSPI status flag polling mode + DPRINTF("QSPI: CMD=0x%02X wait\n", s->INSTRUCTION); + assert(s->PMM == 0); + assert(s->APMS == 1); + + qemu_irq_lower(s->cs); + while (true) + { + ssi_transfer(s->qspi, s->INSTRUCTION); + uint32_t response = 0; + for (int i = 0; i < 4; i++) + { + response |= ssi_transfer(s->qspi, 0) << 8 * i; + } + + if ((response & s->MASK) == s->MATCH) + { + // match found + s->SMF = true; + // break + break; + } + } + qemu_irq_raise(s->cs); + } + else + { + assert(false); + } +} + +static void +stm32l476_quadspi_reset(DeviceState *dev) +{ + STM32L476QspiState *s = STM32L476_QSPI(dev); +} + +static uint64_t +stm32l476_quadspi_read(void *opaque, hwaddr offset, + unsigned int size) +{ + STM32L476QspiState *s = opaque; +// 0801ce1e + switch (offset) + { + case QUADSPI_CR:DPRINTF("QSPI: read CR\n"); + return 0; + + case QUADSPI_DCR:DPRINTF("QSPI: read DCR\n"); + return 0; + + case QUADSPI_SR: + { + uint32_t out = 0; + out |= s->TEF << 0; + out |= s->TCF << 1; + + // FTF + if (s->FMODE == 0b01) + { + out |= (!fifo8_is_empty(&s->rx_fifo)) << 2; + } + else if (s->FMODE == 0b00) + { + out |= 1 << 2; + } + + out |= s->SMF << 3; + out |= s->TOF << 4; + out |= s->BUSY << 5; + DPRINTF("QSPI: read SR (0x%X)\n", out); + return out; + } + + case QUADSPI_DLR:DPRINTF("QSPI: read DLR\n"); + return s->DL; + + case QUADSPI_CCR: + { + uint32_t out = 0; + out |= s->INSTRUCTION << 0; + out |= s->IMODE << 8; + out |= s->ADMODE << 10; + out |= s->ADSIZE << 12; + out |= s->ABMODE << 14; + out |= s->ABSIZE << 16; + out |= s->DCYC << 18; + out |= s->DMODE << 24; + out |= s->FMODE << 26; + return out; + } + + case QUADSPI_AR:DPRINTF("QSPI: read AR\n"); + return s->ADDRESS; + + case QUADSPI_DR: + if (size == 1) + { + uint8_t kI = fifo8_pop(&s->rx_fifo); + DPRINTF("QSPI: read DR (0x%02X)\n", kI); + if (fifo8_is_empty(&s->rx_fifo)) + { + s->BUSY = false; + s->TCF = true; + } + return kI; + } + else + { + assert(false); + } + break; + + default:qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + return 0; +} + +static void +stm32l476_quadspi_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned int size) +{ + STM32L476QspiState *s = opaque; + StartMode kStart; + + switch (offset) + { + case QUADSPI_CR:DPRINTF("QSPI: CR = 0x%08X\n", val64); + s->EN = extract64(val64, 0, 1); + s->ABORT = extract64(val64, 1, 1); + s->APMS = extract64(val64, 22, 1); + s->PMM = extract64(val64, 23, 1); + + if (s->ABORT) + { + s->TCF = true; + s->ABORT = false; + } + + break; + + case QUADSPI_DCR:DPRINTF("QSPI: DCR = 0x%08X\n", val64); + break; + + case QUADSPI_FCR:DPRINTF("QSPI: FCR = 0x%08X\n", val64); + if (val64 & (1 << 0)) + s->TEF = false; + if (val64 & (1 << 1)) + s->TCF = false; + if (val64 & (1 << 3)) + s->SMF = false; + if (val64 & (1 << 4)) + s->TOF = false; + break; + + case QUADSPI_DLR:s->DL = extract64(val64, 0, 32); + break; + + case QUADSPI_CCR:s->INSTRUCTION = extract64(val64, 0, 8); + s->IMODE = extract64(val64, 8, 2); + s->ADMODE = extract64(val64, 10, 2); + s->ADSIZE = extract64(val64, 12, 2); + s->ABMODE = extract64(val64, 14, 2); + s->ABSIZE = extract64(val64, 16, 2); + s->DCYC = extract64(val64, 18, 5); + s->DMODE = extract64(val64, 24, 2); + s->FMODE = extract64(val64, 26, 2); + + kStart = getStart(s); + if (kStart == OnWriteCCR) + { + start(s); + } + else + { + DPRINTF("Waiting to start with cmd 0x%X\n", s->INSTRUCTION); + } + + break; + + case QUADSPI_AR:s->ADDRESS = extract64(val64, 0, 32); + kStart = getStart(s); + if (kStart == OnWriteAR) + { + start(s); + } + break; + + case QUADSPI_DR: + if (size == 1) + { + kStart = getStart(s); + if (kStart == OnWriteDR) + { + start(s); + } + assert(s->FMODE == 0b00); + ssi_transfer(s->qspi, val64); + s->dataPushed++; + + if (s->dataPushed - 1 == s->DL) + { + s->TCF = true; + s->BUSY = false; + } + } + else + { + assert(false); + } + break; + + case QUADSPI_PSMKR: + if (!s->BUSY) + { + s->MASK = extract64(val64, 0, 32); + } + break; + + case QUADSPI_PSMAR: + if (!s->BUSY) + { + s->MATCH = extract64(val64, 0, 32); + } + break; + + case QUADSPI_PIR: + if (!s->BUSY) + { + s->INTERVAL = extract64(val64, 0, 16); + } + break; + + default:qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, val64, offset); + break; + } +} + +static const MemoryRegionOps stm32l476_quadspi_ops = { + .read = stm32l476_quadspi_read, + .write = stm32l476_quadspi_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static uint64_t +cache_read(void *opaque, hwaddr offset, + unsigned int size) +{ + STM32L476QspiState *s = opaque; + + switch (offset) + { + default:qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + return 0; +} +static void +cache_write(void *opaque, hwaddr offset, uint64_t val64, unsigned int size) +{ + + STM32L476QspiState *s = opaque; + + switch (offset) + { + default:qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, val64, offset); + break; + } +} + +static const MemoryRegionOps cache_ops = { + .read = cache_read, + .write = cache_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void +stm32l476_quadspi_init(DeviceState *dev, Error **pError) +{ + STM32L476QspiState *s = STM32L476_QSPI(dev); + + memory_region_init_io(&s->cache, dev, &cache_ops, s, "QSPI", 0x10000000); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->cache); + + memory_region_init_io(&s->mmio, dev, &stm32l476_quadspi_ops, s, + "QPSI_R", 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); + + s->qspi = ssi_create_bus(dev, "qspi"); + + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs); + + fifo8_create(&s->rx_fifo, 1024 * 1024); +} + +static void +stm32l476_quadspi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_quadspi_reset; + dc->realize = stm32l476_quadspi_init; +} + +static const TypeInfo stm32l476_quadspi_info = { + .name = TYPE_STM32L476_QSPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476QspiState), + .class_init = stm32l476_quadspi_class_init, +}; + +static void +stm32l476_quadspi_register_types(void) +{ + type_register_static(&stm32l476_quadspi_info); +} + +type_init(stm32l476_quadspi_register_types) diff --git a/hw/arm/stm32l476_rcc.c b/hw/arm/stm32l476_rcc.c new file mode 100644 index 0000000000000..0daa17071df07 --- /dev/null +++ b/hw/arm/stm32l476_rcc.c @@ -0,0 +1,168 @@ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" + +#include "hw/arm/stm32l476_rcc.h" + +#define RCC_CR 0x00 +#define RCC_ICSCR 0x04 +#define CFGR 0x08 +#define RCC_PLLCFGR 0x0C +#define PLLSAI1CFGR 0x10 +#define PLLSAI2CFGR 0x14 +#define RCC_CIER 0x18 +#define CIFR 0x1C +#define CICR 0x20 +#define RESERVED0 0x24 +#define AHB1RSTR 0x28 +#define AHB2RSTR 0x2C +#define AHB3RSTR 0x30 +#define RESERVED1 0x34 +#define RCC_APB1RSTR1 0x38 +#define APB1RSTR2 0x3C +#define APB2RSTR 0x40 +#define RESERVED2 0x44 +#define AHB1ENR 0x48 +#define AHB2ENR 0x4C +#define AHB3ENR 0x50 +#define RESERVED3 0x54 +#define RCC_APB1ENR1 0x58 +#define APB1ENR2 0x5C +#define RCC_APB2ENR 0x60 +#define RESERVED4 0x64 +#define AHB1SMENR 0x68 +#define AHB2SMENR 0x6C +#define AHB3SMENR 0x70 +#define RESERVED5 0x74 +#define APB1SMENR1 0x78 +#define APB1SMENR2 0x7C +#define APB2SMENR 0x80 +#define RESERVED6 0x84 +#define RCC_CCIPR 0x88 +#define RESERVED7 0x8C +#define BDCR 0x90 +#define CSR 0x94 + +static void stm32l476_rcc_reset(DeviceState *dev) { + STM32L476RccState *s = STM32L476_RCC(dev); + s->acr_latency = 0; + + // + s->LSEON = false; + s->LSEBYP = false; +} + +static uint64_t stm32l476_rcc_read(void *opaque, hwaddr offset, + unsigned int size) { + STM32L476RccState *s = opaque; + + switch (offset) { + case RCC_CR: { + uint32_t out = 0; + out |= 1 << 17; // HSERDY + out |= 1 << 16; // HSEON + out |= 1 << 10; // HSIRDY + out |= 1 << 8; // HSION + out |= 1 << 1; // MSIRDY + return out; + } + + case CFGR: { + uint32_t out = 0; + out |= s->SW << 2; + return out; + } + + case BDCR: { + uint32_t out = 0; + out |= s->LSEON << 0; + if (s->LSEON) + out |= 0b1 << 1; + out |= s->LSEBYP << 2; + return out; + } + + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + return 0; +} + +static void stm32l476_rcc_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned int size) { + STM32L476RccState *s = opaque; + + switch (offset) { + case RCC_CR: + case RCC_ICSCR: + case RCC_PLLCFGR: + case RCC_CIER: + case RCC_APB1RSTR1: + case RCC_APB1ENR1: + case RCC_APB2ENR: + break; + case RCC_CCIPR: + { + printf("CCIPR: 0x%08X\n", val64); + break; + } + + case CFGR: { + s->SW = (val64 >> 0) & 0b11; + break; + } + + case BDCR: { + s->LSEON = (val64 >> 0) & 0b1; + s->LSEBYP = (val64 >> 2) & 0b1; + break; + } + + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, val64, offset); + break; + } +} + +static const MemoryRegionOps stm32l476_rcc_ops = { + .read = stm32l476_rcc_read, + .write = stm32l476_rcc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32l476_rcc_init(Object *obj) { + STM32L476RccState *s = STM32L476_RCC(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l476_rcc_ops, s, + TYPE_STM32L476_RCC, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void stm32l476_rcc_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_rcc_reset; +} + +static const TypeInfo stm32l476_rcc_info = { + .name = TYPE_STM32L476_RCC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476RccState), + .instance_init = stm32l476_rcc_init, + .class_init = stm32l476_rcc_class_init, +}; + +static void stm32l476_rcc_register_types(void) { + type_register_static(&stm32l476_rcc_info); +} + +type_init(stm32l476_rcc_register_types) diff --git a/hw/arm/stm32l476_spi.c b/hw/arm/stm32l476_spi.c new file mode 100644 index 0000000000000..405effd315081 --- /dev/null +++ b/hw/arm/stm32l476_spi.c @@ -0,0 +1,136 @@ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" +#include "hw/ssi/ssi.h" +#include "hw/arm/stm32l476_spi.h" +#include "hw/qdev-properties.h" +#include "hw/irq.h" + +#define SPIx_CR1 0x00 +#define SPIx_CR2 0x04 +#define SPIx_SR 0x08 +#define SPIx_DR 0x0C + +static void stm32l476_spi_set_txdmaen(STM32L476SpiState *s, bool enabled) { + if(s->txdmaen != enabled) { + s->txdmaen = enabled; + qemu_set_irq(s->txdrq, enabled); + } +} + +static void stm32l476_spi_reset(DeviceState *dev) { + STM32L476SpiState *s = STM32L476_SPI(dev); +} + +static uint64_t stm32l476_spi_read(void *opaque, hwaddr offset, + unsigned int size) { + STM32L476SpiState *s = opaque; + + switch (offset) { + case SPIx_CR1: + break; + case SPIx_CR2: { + uint32_t out = 0; + out |= s->txdmaen << 1; + return out; + } + + case SPIx_SR: { + uint32_t out = 0; + out |= 0 << 0; //RXNE + out |= 1 << 1; // TXE + out |= 0 << 4; // CRCERR + out |= 0 << 5; // MODF + out |= 0 << 6; // OVR + out |= 0 << 7; // BSY + out |= 0 << 8; // FRE + out |= 0 << 9; // FRLVL + out |= 0 << 11; // FTLVL + return out; + } + + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + return 0; +} + +static void stm32l476_spi_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned int size) { + STM32L476SpiState *s = opaque; + + switch (offset) { + case SPIx_CR1: + break; + case SPIx_CR2: + stm32l476_spi_set_txdmaen(s, extract64(val64, 1, 1)); + break; + case SPIx_DR: + { + if (size == 1) + { + ssi_transfer(s->spi, val64); + } + else + { + ssi_transfer(s->spi, val64 & 0xFF); + ssi_transfer(s->spi, (val64 >> 8) & 0xFF); + } + break; + } + + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, val64, offset); + break; + } +} + +static const MemoryRegionOps stm32l476_spi_ops = { + .read = stm32l476_spi_read, + .write = stm32l476_spi_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32l476_spi_init(Object *obj) { + STM32L476SpiState *s = STM32L476_SPI(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l476_spi_ops, s, + TYPE_STM32L476_SPI, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + s->spi = ssi_create_bus(DEVICE(obj), "ssi"); +} + +static Property intel_hda_properties[] = { + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32l476_spi_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_spi_reset; + device_class_set_props(dc, intel_hda_properties); +} + +static const TypeInfo stm32l476_spi_info = { + .name = TYPE_STM32L476_SPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476SpiState), + .instance_init = stm32l476_spi_init, + .class_init = stm32l476_spi_class_init, +}; + +static void stm32l476_spi_register_types(void) { + type_register_static(&stm32l476_spi_info); +} + +type_init(stm32l476_spi_register_types) diff --git a/hw/arm/stm32l476_syscfg.c b/hw/arm/stm32l476_syscfg.c new file mode 100644 index 0000000000000..d052d02037bbf --- /dev/null +++ b/hw/arm/stm32l476_syscfg.c @@ -0,0 +1,148 @@ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" + +typedef struct +{ + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + + uint8_t EXTI[16]; +} STM32L476SysCfgState; + +#define TYPE_STM32L476_SYSCFG "stm32l476-syscfg" + +#define STM32L476_SYSCFG(obj) \ + OBJECT_CHECK(STM32L476SysCfgState, (obj), TYPE_STM32L476_SYSCFG) + +static const int SYSCFG_EXTICR1 = 0x08; +static const int SYSCFG_EXTICR2 = 0x0c; +static const int SYSCFG_EXTICR4 = 0x14; +static const int SYSCFG_EXTICR3 = 0x10; + + +static void +stm32l476_syscfg_reset(DeviceState *dev) +{ + STM32L476SysCfgState *s = STM32L476_SYSCFG(dev); + + for (int i = 0; i < 16; i++) + { + s->EXTI[i] = 0b1111; + } + +} + +static uint32_t +getInt(const STM32L476SysCfgState *s, uint32_t offset) +{ + uint32_t out = 0; + for (int i = 0; i < 4; i++) + { + out |= s->EXTI[i + offset] << (i * 4); + } + return out; +} +static uint64_t +stm32l476_syscfg_read(void *opaque, hwaddr offset, + unsigned size) +{ + STM32L476SysCfgState *s = STM32L476_SYSCFG(opaque); + + switch (offset) + { + case 0x04:break; + case SYSCFG_EXTICR1: + { + return getInt(s, 0); + } + case SYSCFG_EXTICR2: + { + return getInt(s, 4); + } + case SYSCFG_EXTICR3: + { + return getInt(s, 8); + } + case SYSCFG_EXTICR4: + { + return getInt(s, 12); + } + + default: + { + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + } + return 0; +} + +static void +stm32l476_syscfg_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + switch (offset) + { + case 0x04: +// case SYSCFG_EXTICR1: +// case SYSCFG_EXTICR2: +// case SYSCFG_EXTICR4: + break; + + default:qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, val64, offset); + break; + + } +} + +static const MemoryRegionOps stm32l476_syscfg_ops = { + .read = stm32l476_syscfg_read, + .write =stm32l476_syscfg_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void +stm32l476_syscfg_init(Object *obj) +{ + STM32L476SysCfgState *s = STM32L476_SYSCFG(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l476_syscfg_ops, s, + TYPE_STM32L476_SYSCFG, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void +stm32l476_syscfg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_syscfg_reset; +} + +static const TypeInfo stm32l476_syscfg_info = { + .name = TYPE_STM32L476_SYSCFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476SysCfgState), + .instance_init = stm32l476_syscfg_init, + .class_init = stm32l476_syscfg_class_init, +}; + +static void +stm32l476_syscfg_register_types(void) +{ + type_register_static(&stm32l476_syscfg_info); +} + +type_init(stm32l476_syscfg_register_types) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 82270884416e3..09d14aaca924c 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -313,6 +313,7 @@ static const FlashPartInfo known_devices[] = { { INFO("w25q32", 0xef4016, 0, 64 << 10, 64, ER_4K) }, { INFO("w25q32dw", 0xef6016, 0, 64 << 10, 64, ER_4K) }, { INFO("w25x64", 0xef3017, 0, 64 << 10, 128, ER_4K) }, + { INFO("w25q64fw", 0xef0016, 0, 64 << 10, 128, ER_4K) }, { INFO("w25q64", 0xef4017, 0, 64 << 10, 128, ER_4K) }, { INFO("w25q80", 0xef5014, 0, 64 << 10, 16, ER_4K) }, { INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) }, @@ -721,6 +722,14 @@ static void complete_collecting_data(Flash *s) break; case RDID_90: case RDID_AB: + if(get_man(s) == MAN_WINBOND) { + s->data[0] = s->pi->id[0]; + s->data[1] = s->pi->id[2]; + s->pos = 0; + s->len = 2; + s->data_read_loop = true; + s->state = STATE_READING_DATA; + } if (get_man(s) == MAN_SST) { if (s->cur_addr <= 1) { if (s->cur_addr) { diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 77a7d622bd2da..a7763d3ca252b 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -60,3 +60,5 @@ common-obj-$(CONFIG_DPCD) += dpcd.o common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dp.o common-obj-$(CONFIG_ATI_VGA) += ati.o ati_2d.o ati_dbg.o + +obj-y += jdi-lpm013m126c.o \ No newline at end of file diff --git a/hw/display/jdi-lpm013m126c.c b/hw/display/jdi-lpm013m126c.c new file mode 100644 index 0000000000000..d016881f696f1 --- /dev/null +++ b/hw/display/jdi-lpm013m126c.c @@ -0,0 +1,245 @@ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/arm/boot.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" +#include "hw/ssi/ssi.h" +#include "ui/console.h" +#include "ui/pixel_ops.h" + +typedef struct { + uint8_t r; + uint8_t g; + uint8_t b; +} rgb; + +typedef struct { + SSISlave ssidev; + QemuConsole *con; + + int state; + uint16_t header; + + rgb buffer[176][176]; + uint32_t fifo; + uint32_t fifo_len; + uint32_t bpp; + int cur_x; + int line; +} lcd_state; + +#define TYPE_LCD "jdi-lpm013m126c" +#define LCD(obj) \ + OBJECT_CHECK(lcd_state, (obj), TYPE_LCD) + +#define M0_HI(x) ((((x) >> 0) & 1) != 0) +#define M1_HI(x) ((((x) >> 1) & 1) != 0) +#define M2_HI(x) ((((x) >> 2) & 1) != 0) +#define M3_HI(x) ((((x) >> 3) & 1) != 0) +#define M4_HI(x) ((((x) >> 4) & 1) != 0) +#define M5_HI(x) ((((x) >> 5) & 1) != 0) + +#define M0_LOW(x) ((((x) >> 0) & 1) == 0) +#define M1_LOW(x) ((((x) >> 1) & 1) == 0) +#define M2_LOW(x) ((((x) >> 2) & 1) == 0) +#define M3_LOW(x) ((((x) >> 3) & 1) == 0) +#define M4_LOW(x) ((((x) >> 4) & 1) == 0) +#define M5_LOW(x) ((((x) >> 5) & 1) == 0) + +static uint8_t bitswap8(uint8_t val) { + return ((val * 0x0802LU & 0x22110LU) | (val * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16; +} + +uint32_t parse_header(lcd_state *s, uint16_t header) { + if (header == 0x0000 || header == 0xFFFF) + { + // Dummy + s->state = 0; + return 0; + } + + if (M0_LOW(header) && M2_HI(header)) + { + for (int x = 0; x < 176; x++) + { + for (int y = 0; y < 176; y++) + { + s->buffer[y][x].r = x % 2; + s->buffer[y][x].g = y % 2; + s->buffer[y][x].b = 0; + } + } + s->state = 0; + return 0; + } + + if (M0_HI(header) && M2_LOW(header)) + { + // Update + + s->state = 2; + s->line = bitswap8(header >> 8) - 1; + s->cur_x = 0; + if (M3_HI(header)) + { + s->bpp = 4; + } + else if (M4_HI(header)) + { + s->bpp = 1; + } + else + { + s->bpp = 3; + } + + return 0; + } + else + { + assert(false); + } + + s->state = 0; + return 0; +} + +uint32_t jdi_lpm013m126c_transfer(SSISlave *bus, uint32_t val32) +{ + lcd_state *s = LCD(bus); + uint8_t val = val32 & 0xFF; + + if (s->state == -1) + { + return 0; + } + + if (s->state == 0) + { + s->header = val; + s->state = 1; + return 0; + } + + + if (s->state == 1) + { + s->header |= val << 8; + + s->state = -1; + parse_header(s, s->header); + assert(s->state != -1); + return 0; + } + + if (s->state == 2) { + s->fifo |= val << s->fifo_len; + s->fifo_len += 8; + + while (s->fifo_len >= s->bpp) { + + int r, g, b; + if (s->bpp == 1) { + r = g = b = s->fifo & 1; + } else { + r = (s->fifo >> 0) & 1; + g = (s->fifo >> 1) & 1; + b = (s->fifo >> 2) & 1; + } + + if(s->cur_x < 176) + { + s->buffer[s->line][s->cur_x].r = r; + s->buffer[s->line][s->cur_x].g = g; + s->buffer[s->line][s->cur_x].b = b; + } + s->fifo = s->fifo >> s->bpp; + s->fifo_len -= s->bpp; + + s->cur_x++; + if (s->cur_x == 176) { + s->state = 0; + return 0; + } + } + } +} + +static void sm_lcd_update_display(void *arg) { + lcd_state *s = arg; + + DisplaySurface *surface = qemu_console_surface(s->con); + int bpp = surface_bits_per_pixel(surface); + void *d = surface_data(surface); + + assert(bpp == 32); + + for (int y = 0; y < 176; y++) { + for (int x = 0; x < 176; x++) { + + *((uint32_t *) d) = rgb_to_pixel32( + s->buffer[y][x].r * 255, + s->buffer[y][x].g * 255, + s->buffer[y][x].b * 255); + d += 4; + } + } + dpy_gfx_update(s->con, 0, 0, 176, 176); +} + +static void sm_lcd_invalidate_display(void *arg) { + lcd_state *s = arg; +} + +static const GraphicHwOps sm_lcd_ops = { + .gfx_update = sm_lcd_update_display, + .invalidate = sm_lcd_invalidate_display, +}; + +static void +jdi_lpm013m126c_realize(SSISlave *d, Error **errp) +{ + + DeviceState *dev = DEVICE(d); + lcd_state *s = LCD(dev); + + s->con = graphic_console_init(dev, 0, &sm_lcd_ops, s); + qemu_console_resize(s->con, 176, 176); +} + +int +jdi_lpm013m126c_set_cd(SSISlave *dev, bool select) +{ + lcd_state *s = LCD(dev); + + if (select) + { + s->state = 0; + } + + return 0; +} + +static void +jdi_lpm013m126c_class_init(ObjectClass *klass, void *data) +{ + SSISlaveClass *k = SSI_SLAVE_CLASS(klass); + k->transfer = jdi_lpm013m126c_transfer; + k->realize = jdi_lpm013m126c_realize; + k->cs_polarity = SSI_CS_HIGH; + k->set_cs = jdi_lpm013m126c_set_cd; +} + +static const TypeInfo jdi_lpm013m126c_info = { + .name = TYPE_LCD, + .parent = TYPE_SSI_SLAVE, + .instance_size = sizeof(lcd_state), + .class_init = jdi_lpm013m126c_class_init, +}; + +static void jdi_lpm013m126c_register_types(void) { + type_register_static(&jdi_lpm013m126c_info); +} + +type_init(jdi_lpm013m126c_register_types) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 1ad35e55292b7..00d03411eddd3 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2638,7 +2638,7 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp) /* include space for internal exception vectors */ s->num_irq += NVIC_FIRST_IRQ; - s->num_prio_bits = arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 8 : 2; + s->num_prio_bits = 4; object_property_set_bool(OBJECT(&s->systick[M_REG_NS]), true, "realized", &err); diff --git a/include/hw/arm/stm32l467_soc.h b/include/hw/arm/stm32l467_soc.h new file mode 100644 index 0000000000000..1a34da278c044 --- /dev/null +++ b/include/hw/arm/stm32l467_soc.h @@ -0,0 +1,45 @@ +#ifndef HW_ARM_STM32L467_SOC_H +#define HW_ARM_STM32L467_SOC_H + +#include "hw/arm/armv7m.h" +#include "hw/arm/stm32l476_flash.h" +#include "hw/arm/stm32l476_lptim.h" +#include "hw/arm/stm32l476_pwr.h" +#include "hw/arm/stm32l476_rcc.h" +#include "hw/arm/stm32l476_dma.h" +#include "hw/arm/stm32l476_spi.h" +#include "hw/arm/stm32l476_i2c.h" +#include "hw/arm/stm32l476_qspi.h" + +#define TYPE_STM32L467_SOC "stm32l467-soc" +#define STM32L467_SOC(obj) \ + OBJECT_CHECK(STM32L467State, (obj), TYPE_STM32L467_SOC) + +#define FLASH_BASE_ADDRESS 0x8000000 +#define FLASH_SIZE (1024 * 1024) + + +typedef struct STM32L467State { + SysBusDevice parent_obj; + + char *cpu_type; + + ARMv7MState armv7m; + STM32L476PwrState pwr; + STM32L476LPTimState lptim1; + STM32L476RccState rcc; + STM32L476FlashState flash_r; + STM32L476DmaState dma; + STM32L476SpiState spi[3]; + STM32L476QspiState qspi; + STM32L476I2CState i2c[3]; +// STM32L476TimState tim2; + MemoryRegion flash_alias; + MemoryRegion flash; + MemoryRegion sram1; + MemoryRegion sram2; + DeviceState* gpio[8]; + DeviceState *syscfg; +} STM32L467State; + +#endif diff --git a/include/hw/arm/stm32l476_dma.h b/include/hw/arm/stm32l476_dma.h new file mode 100644 index 0000000000000..3d68d8125703b --- /dev/null +++ b/include/hw/arm/stm32l476_dma.h @@ -0,0 +1,48 @@ +#ifndef HW_ARM_STM32L476_DMA_H +#define HW_ARM_STM32L476_DMA_H + +#include "hw/arm/armv7m.h" + +#define TYPE_STM32L476_DMA "stm32l476-dma" +#define STM32L476_DMA(obj) \ + OBJECT_CHECK(STM32L476DmaState, (obj), TYPE_STM32L476_DMA) + +typedef struct _STM32L476DmaState STM32L476DmaState; + +typedef struct STM32L476DmaChannelState { + STM32L476DmaState *parent; + uint8_t CxS; + uint16_t NDTR; + uint32_t PA; + uint32_t MA; + qemu_irq *drq; + bool EN; + bool TCIE; + bool HTIE; + bool TEIE; + bool CIRC; + bool PINC; + bool MINC; + bool MEM2MEM; + uint64_t DIR; + uint64_t PSIZE; + uint64_t MSIZE; + uint64_t PL; + int id; + bool TCIF; + bool HTIF; + bool TEIF; +} STM32L476DmaChannelState; + +struct _STM32L476DmaState { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + qemu_irq irq[7]; + STM32L476DmaChannelState channels[7]; + +}; + +#endif diff --git a/include/hw/arm/stm32l476_flash.h b/include/hw/arm/stm32l476_flash.h new file mode 100644 index 0000000000000..1f17be684082e --- /dev/null +++ b/include/hw/arm/stm32l476_flash.h @@ -0,0 +1,19 @@ +#ifndef HW_ARM_STM32L476_FLASH_H +#define HW_ARM_STM32L476_FLASH_H + +#include "hw/arm/armv7m.h" + +#define TYPE_STM32L476_FLASH "stm32l476-flash" +#define STM32L476_FLASH(obj) \ + OBJECT_CHECK(STM32L476FlashState, (obj), TYPE_STM32L476_FLASH) + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + uint8_t acr_latency; +} STM32L476FlashState; + +#endif diff --git a/include/hw/arm/stm32l476_gpio.h b/include/hw/arm/stm32l476_gpio.h new file mode 100644 index 0000000000000..d084fa5a9e094 --- /dev/null +++ b/include/hw/arm/stm32l476_gpio.h @@ -0,0 +1,12 @@ +#ifndef HW_ARM_STM32L476_GPIO_H +#define HW_ARM_STM32L476_GPIO_H + +#include "hw/arm/armv7m.h" + +#define TYPE_STM32L476_GPIO "stm32l476-gpio" +#define STM32L476_GPIO(obj) \ + OBJECT_CHECK(STM32L476GpioState, (obj), TYPE_STM32L476_GPIO) + +typedef struct __STM32L476GpioState STM32L476GpioState; +static const int GPIOx_AFRL = 0x20; +#endif diff --git a/include/hw/arm/stm32l476_i2c.h b/include/hw/arm/stm32l476_i2c.h new file mode 100644 index 0000000000000..31f94d83a7b0b --- /dev/null +++ b/include/hw/arm/stm32l476_i2c.h @@ -0,0 +1,38 @@ +#ifndef HW_ARM_STM32L476_I2C_H +#define HW_ARM_STM32L476_I2C_H + +#include "hw/arm/armv7m.h" + +#define TYPE_STM32L476_I2C "stm32l476-i2c" +#define STM32L476_I2C(obj) \ + OBJECT_CHECK(STM32L476I2CState, (obj), TYPE_STM32L476_I2C) + +typedef struct { + /* */ + SysBusDevice parent_obj; + char *name; + + /* */ + MemoryRegion mmio; + qemu_irq irq; + + // I2C_CR2 + uint64_t SADD; + uint64_t RD_WRN; + uint64_t ADD10; + uint64_t HEAD10R; + bool START; + bool STOP; + bool NACK; + uint64_t NBYTES; + uint64_t RELOAD; + uint64_t AUTOEND; + bool PECBYTE; + + // I2C_ISR + bool TXIS; + bool BUSY; + I2CBus *bus; +} STM32L476I2CState; + +#endif diff --git a/include/hw/arm/stm32l476_lptim.h b/include/hw/arm/stm32l476_lptim.h new file mode 100644 index 0000000000000..82efd2a04d0d6 --- /dev/null +++ b/include/hw/arm/stm32l476_lptim.h @@ -0,0 +1,50 @@ +#ifndef HW_ARM_STM32L476_LPTIM_H +#define HW_ARM_STM32L476_LPTIM_H + +#include "hw/arm/armv7m.h" +#include "hw/ptimer.h" + +#define TYPE_STM32L476_LPTIM "stm32l476-lptim" +#define STM32L476_LPTIM(obj) \ + OBJECT_CHECK(STM32L476LPTimState, (obj), TYPE_STM32L476_LPTIM) + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + qemu_irq irq; + + int64_t start; + QEMUTimer *timer; + ptimer_state * ptimer; + + // ISR + bool CMPM; + bool ARRM; + bool EXTTRIG; + bool CMPOK; + bool ARROK; + bool UP; + bool DOWN; + + // IER: + bool CMPMIE; + bool ARRMIE; + bool EXTTRIGIE; + bool CMPOKIE; + bool ARROKIE; + bool UPIE; + bool DOWNIE; + + // CR + bool ENABLE; + bool SNGSTRT; + bool CNTSTRT; + + uint16_t CNT; + uint16_t ARR; +} STM32L476LPTimState; + +#endif diff --git a/include/hw/arm/stm32l476_pwr.h b/include/hw/arm/stm32l476_pwr.h new file mode 100644 index 0000000000000..be28154559775 --- /dev/null +++ b/include/hw/arm/stm32l476_pwr.h @@ -0,0 +1,34 @@ +#ifndef HW_ARM_STM32L476_PWR_H +#define HW_ARM_STM32L476_PWR_H + +#include "hw/arm/armv7m.h" + +#define TYPE_STM32L476_PWR "stm32l476-pwr" +#define STM32L476_PWR(obj) \ + OBJECT_CHECK(STM32L476PwrState, (obj), TYPE_STM32L476_PWR) + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + + // CR1 + uint8_t LPMS; + bool DBP; + uint8_t VOS; + bool LPR; + + // SR2 + bool REGLPS; + bool REGLPF; + bool VOSF; + bool PVDO; + bool PVMO1; + bool PVMO2; + bool PVMO3; + bool PVMO4; +} STM32L476PwrState; + +#endif diff --git a/include/hw/arm/stm32l476_qspi.h b/include/hw/arm/stm32l476_qspi.h new file mode 100644 index 0000000000000..c476665d5cf9c --- /dev/null +++ b/include/hw/arm/stm32l476_qspi.h @@ -0,0 +1,56 @@ +#ifndef HW_ARM_STM32L476_QSPI_H +#define HW_ARM_STM32L476_QSPI_H + +#include "hw/sysbus.h" +#include "hw/ssi/ssi.h" +#include "qemu/fifo8.h" + +#define TYPE_STM32L476_QSPI "stm32l476-qspi" +#define STM32L476_QSPI(obj) \ + OBJECT_CHECK(STM32L476QspiState, (obj), TYPE_STM32L476_QSPI) + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + MemoryRegion cache; + + bool EN; + bool ABORT; + + bool TCF; + bool TEF; + bool SMF; + bool TOF; + + uint8_t PMM; + uint8_t APMS; + + uint32_t DL; + uint8_t INSTRUCTION; + uint64_t IMODE; + uint64_t ADMODE; + uint64_t ADSIZE; + uint64_t ABMODE; + uint64_t ABSIZE; + uint64_t DCYC; + uint64_t DMODE; + uint64_t FMODE; + uint32_t MASK; + uint32_t MATCH; + uint16_t INTERVAL; + + uint32_t ADDRESS; + bool BUSY; + + qemu_irq irq; + SSIBus *qspi; + qemu_irq cs; + + Fifo8 rx_fifo; + int dataPushed; +} STM32L476QspiState; + +#endif diff --git a/include/hw/arm/stm32l476_rcc.h b/include/hw/arm/stm32l476_rcc.h new file mode 100644 index 0000000000000..e0c887843e811 --- /dev/null +++ b/include/hw/arm/stm32l476_rcc.h @@ -0,0 +1,26 @@ +#ifndef HW_ARM_STM32L476_RCC_H +#define HW_ARM_STM32L476_RCC_H + +#include "hw/arm/armv7m.h" + +#define TYPE_STM32L476_RCC "stm32l476-rcc" +#define STM32L476_RCC(obj) \ + OBJECT_CHECK(STM32L476RccState, (obj), TYPE_STM32L476_RCC) + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + uint8_t acr_latency; + + // CFGR + uint8_t SW; + + // BDCR + bool LSEON; + bool LSEBYP; +} STM32L476RccState; + +#endif diff --git a/include/hw/arm/stm32l476_spi.h b/include/hw/arm/stm32l476_spi.h new file mode 100644 index 0000000000000..269583da55bc0 --- /dev/null +++ b/include/hw/arm/stm32l476_spi.h @@ -0,0 +1,27 @@ +#ifndef HW_ARM_STM32L476_SPI_H +#define HW_ARM_STM32L476_SPI_H + +#include "hw/arm/armv7m.h" + +#define TYPE_STM32L476_SPI "stm32l476-spi" +#define STM32L476_SPI(obj) \ + OBJECT_CHECK(STM32L476SpiState, (obj), TYPE_STM32L476_SPI) + + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + qemu_irq irq; + + SSIBus *spi; + + qemu_irq rxdrq; + qemu_irq txdrq; + + bool txdmaen; +} STM32L476SpiState; + +#endif diff --git a/sram1.bin b/sram1.bin new file mode 100644 index 0000000000000..9ab9b961be2df Binary files /dev/null and b/sram1.bin differ diff --git a/sram2.bin b/sram2.bin new file mode 100644 index 0000000000000..192c71c0e4899 Binary files /dev/null and b/sram2.bin differ diff --git a/ui/cocoa.m b/ui/cocoa.m index cb556e4e6689d..316beaee3997e 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -950,6 +950,7 @@ - (bool) handleEventLocked:(NSEvent *)event - (void) grabMouse { + return; COCOA_DEBUG("QemuCocoaView: grabMouse\n"); if (!isFullscreen) { @@ -968,6 +969,7 @@ - (void) grabMouse - (void) ungrabMouse { + return; COCOA_DEBUG("QemuCocoaView: ungrabMouse\n"); if (!isFullscreen) {