diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000000..5c9a5dc8afc29 --- /dev/null +++ b/.clang-format @@ -0,0 +1,149 @@ +--- +IndentWidth: 4 +--- +Language: Cpp +ColumnLimit: 80 +BreakBeforeBraces: Linux +SpaceBeforeParens: ControlStatements +IncludeBlocks: Merge +IncludeCategories: + - Regex: 'osdep.h"$' + Priority: -2 + - Regex: '^<' + Priority: -1 + - Regex: '.*' + Priority: 1 +--- +Language: JavaScript +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveBitFields: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: All +AlwaysBreakAfterReturnType: AllDefinitions +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: true + IndentBraces: true + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: All +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: false +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: Always +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: c++03 +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE +... + diff --git a/hw/arm/amazfit.c b/hw/arm/amazfit.c new file mode 100644 index 0000000000000..7474184863e96 --- /dev/null +++ b/hw/arm/amazfit.c @@ -0,0 +1,107 @@ + +#include "qemu/osdep.h" +#include "hw/arm/boot.h" +#include "hw/arm/stm32l467_soc.h" +#include "hw/boards.h" +#include "hw/i2c/i2c.h" +#include "hw/irq.h" +#include "hw/loader.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-clock.h" +#include "hw/ssi/ssi.h" +#include "qapi/error.h" +#include "ui/console.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; +} +qemu_irq buttonIrq; +qemu_irq buttonExtiIrq; +qemu_irq touchIrq; +qemu_irq touchExtiIrq; +void amazfit_key_event(void *opaque, int keycode) +{ + bool pressed = (keycode & 0x80) == 0; + printf("KEY: %d | 0x%x\n", keycode & 0x7F, keycode & 0x80); + + switch (keycode & 0x7F) { + case 31: /* S */ + // LOW = pressed, HIGH = unpressed + qemu_set_irq(buttonIrq, !pressed); + qemu_set_irq(buttonExtiIrq, !pressed); + break; + + default: + return; + } +} + +/* Main SYSCLK frequency in Hz (120MHz) */ +#define SYSCLK_FRQ 120000000ULL + +static void bip_init(MachineState *machine) +{ + Clock *sysclk; + + /* This clock doesn't need migration because it is fixed-frequency */ + sysclk = clock_new(OBJECT(machine), "SYSCLK"); + clock_set_hz(sysclk, SYSCLK_FRQ); + + STM32L467State *dev = STM32L467_SOC(qdev_new(TYPE_STM32L467_SOC)); + qdev_connect_clock_in(dev, "sysclk", sysclk); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + SSIBus *spi_bus = (SSIBus *)qdev_get_child_bus(&dev->spi[2], "ssi"); // SPI3 + DeviceState *display = ssi_create_peripheral(spi_bus, "jdi-lpm013m126c"); + + qemu_irq cs_line = qdev_get_gpio_in_named(display, SSI_GPIO_CS, 0); + qdev_connect_gpio_out(dev->gpio['G' - 'A'], 12, cs_line); + + buttonIrq = qdev_get_gpio_in(dev->gpio['A' - 'A'], 0); + buttonExtiIrq = qdev_get_gpio_in(dev->exti, 0); + qemu_add_kbd_event_handler(amazfit_key_event, NULL); + + + touchIrq = qdev_get_gpio_in(dev->gpio['B' - 'B'], 3); + touchExtiIrq = qdev_get_gpio_in(dev->exti, 3); + + /* --- QSPI Flash --------------------------------------------- */ + SSIBus *qspi = (SSIBus *)qdev_get_child_bus(&dev->qspi, "qspi"); + DeviceState *qspi_flash = qdev_new("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); + ssi_realize_and_unref(qspi_flash, qspi, &error_fatal); + + 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); + + if (false) { + load_image_targphys("/Users/Marijn/Projects/_pebble/" + "Amazfitbip-FreeRTOS/bin/lcd_test.bin", + 0, FLASH_SIZE); + } else { + load_image_targphys("/Users/hamster/Projects/Bip/FW/out/rom.bin", 0, + FLASH_SIZE); + } + + I2CBus *i2c = (I2CBus *)qdev_get_child_bus(&dev->i2c[0], "i2c"); + DeviceState *ts = DEVICE(i2c_slave_create_simple(i2c, "it7259", 0x46)); + qdev_connect_gpio_out(ts, 0, qemu_irq_split(touchIrq,touchExtiIrq)); + + 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..a30da5e07abfc --- /dev/null +++ b/hw/arm/it7259.c @@ -0,0 +1,225 @@ +#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" + +enum { + state_first, + state_second, + state_ready_for_read, + state_internal_register_mode, + state_internal_register_mode_data, + + state_read_buffer, + state_read_point_buffer_ready, + state_end, +}; + + +#define BUFFER_QUERY 0b100 +#define BUFFER_POINT 0b111 + +#define TYPE_IT7259 "it7259" +#define IT7259(obj) OBJECT_CHECK(IT7259State, (obj), TYPE_IT7259) + +typedef struct { + I2CSlave parent_obj; + + qemu_irq interrupt; + + int state; + int pos; + uint8_t buffer[256]; + uint8_t buf; + int reserved; + uint8_t rw; + uint8_t reg; + bool isTouching; + bool isChange; + uint8_t x; + uint8_t y; + uint8_t register_addr; +} IT7259State; + + +static void it7259_int_update(IT7259State *s) { + if(s->isChange) { + qemu_irq_pulse(s->interrupt); + } + +} + +static void it7259_reset(IT7259State *s) { + s->state = state_first; + s->pos = 0; +} + +static void it7259_read(IT7259State *s) { + if (s->state == state_internal_register_mode_data) { + s->pos = 0; + } else if (s->state == state_read_buffer) { + s->pos = 0; + if (s->buf == BUFFER_QUERY) { + s->buffer[0] = 0; + s->buffer[0] = s->isTouching << 6; + s->buffer[0] = s->isChange << 7; + } else if (s->buf == BUFFER_POINT) { + memset(s->buffer, 0, sizeof(s->buffer)); + s->buffer[0] = 0b0000 << 4 | s->isTouching << 3 | s->isTouching << 0; + s->buffer[1] = 0; + if (s->isTouching) { + s->buffer[2] = s->y; + s->buffer[3] = 0; // high bits of x/y + s->buffer[4] = s->x; + s->buffer[5] = 0x4; // Normal finger contact. + } + s->isChange =false; + it7259_int_update(s); + } else { + assert(false); + } + + s->pos = 0; + s->state = state_ready_for_read; + } else { printf("Unknown buffer: %d", s->buf); } +} + +static int it7259_event(I2CSlave *i2c, enum i2c_event event) { + IT7259State *s = IT7259(i2c); + + printf("[IT7259] event: %d\n", event); + switch (event) { + case I2C_START_RECV: + it7259_read(s); + break; + case I2C_START_SEND: + it7259_reset(s); + default: + case I2C_FINISH: + break; + } + + return 0; +} + +static uint8_t it7259_rx(I2CSlave *i2c) { + IT7259State *s = IT7259(i2c); + + if (s->state == state_internal_register_mode_data) { + // Only allow single reads + assert(s->pos == 0); + s->pos++; + switch (s->register_addr) { + case 0x32: + return 0x59; + case 0x33: + return 0x72; + default: + printf("[IT7259] Read internal register: 0x%02X\n", s->register_addr); + break; + } + + } else { + + assert(s->state == state_ready_for_read); + printf("[IT7259] read 0x%02X\n", s->buffer[s->pos]); + return s->buffer[s->pos++]; + } + + return 0xFF; +} + +static int it7259_tx(I2CSlave *i2c, uint8_t data) { + IT7259State *s = IT7259(i2c); + + printf("[IT7259] write 0x%02X\n", data); + + if (s->state == state_first) { + uint8_t m = (data >> 4 & 0b1); + uint8_t addr = (data >> 5 & 0b11); + uint8_t rw = (data >> 7 & 0b1); + if (addr == 0b11 && m == 0b1) { + assert(rw == 0b0); // Datasheet only uses 0x70 for the first byte + s->state = state_internal_register_mode; + } else { + s->reserved = data & 0b11111; + s->buf = (data >> 5) & 0b111; +// assert(s->reserved == 0b00000); + if (s->buf == BUFFER_POINT || s->buf == BUFFER_QUERY) { + s->state = state_read_buffer; + } else { + printf("[IT7259] Unknown buffer: %d\n", s->buf); + assert(false); + } + } + } else if (s->state == state_internal_register_mode) { + s->register_addr = data; + s->state = state_internal_register_mode_data; + } else { + s->buffer[s->pos++] = data; + } + + return 0; +} + +static void it7259_ts_event(void *opaque, + int x, int y, int z, int buttons_state) { + IT7259State *s = opaque; + + + if (buttons_state & MOUSE_EVENT_LBUTTON) { + if (!s->isTouching) { + s->isTouching = true; + s->isChange = true; + } + if (s->x != x) { + s->isChange = true; + s->x = (uint8_t) ((float) x / (float) 0x7FFF * 176.0); + } + if (s->y != y) { + s->isChange = true; + s->y = (uint8_t) ((float) y / (float) 0x7FFF * 176.0); + } + printf("[it7259] Mouse: %d, %d\n", s->x, s->y); + } else { + if (s->isTouching) { + s->isTouching = false; + s->isChange = true; + } + } + + it7259_int_update(s); +} + +static void it7259_realize(DeviceState *dev, Error **errp) { + IT7259State *s = IT7259(dev); + + qdev_init_gpio_out(dev, &s->interrupt, 1); + + qemu_add_mouse_event_handler(it7259_ts_event, s, 1, "amazfit-mouse"); + it7259_reset(s); +} + +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/meson.build b/hw/arm/meson.build index 721a8eb8beddc..124dc1361876d 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -59,4 +59,20 @@ arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c', 'smmuv3.c' arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c')) arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c')) +arm_ss.add(files('amazfit.c')) +arm_ss.add(files('stm32l467_soc.c')) +arm_ss.add(files('stm32l476_flash.c')) +arm_ss.add(files('stm32l476_lptim.c')) +arm_ss.add(files('stm32l476_pwr.c')) +arm_ss.add(files('stm32l476_rcc.c')) +arm_ss.add(files('stm32l476_dma.c')) +arm_ss.add(files('stm32l476_spi.c')) +arm_ss.add(files('stm32l476_qspi.c')) +arm_ss.add(files('stm32l476_gpio.c')) +arm_ss.add(files('stm32l476_syscfg.c')) +arm_ss.add(files('stm32l476_i2c.c')) +arm_ss.add(files('stm32l476_exti.c')) +arm_ss.add(files('it7259.c')) + + hw_arch += {'arm': arm_ss} diff --git a/hw/arm/stm32l467_soc.c b/hw/arm/stm32l467_soc.c new file mode 100644 index 0000000000000..aac437730fb84 --- /dev/null +++ b/hw/arm/stm32l467_soc.c @@ -0,0 +1,458 @@ + +#include "qemu/osdep.h" +#include "hw/arm/stm32l467_soc.h" +#include "exec/address-spaces.h" +#include "hw/arm/armv7m.h" +#include "hw/arm/boot.h" +#include "hw/arm/stm32l476_exti.h" +#include "hw/arm/stm32l476_gpio.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/misc/unimp.h" +#include "hw/sysbus.h" +#include "hw/qdev-clock.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "qom/object.h" +#include "sysemu/sysemu.h" + +#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) + +/*! QUADSPI memories accessible over AHB base address */ +#define QSPI_BASE (0x90000000UL) + +/*! QUADSPI control registers base address */ +#define QSPI_R_BASE (0xA0001000UL) + +enum { + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + + 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_new(TYPE_UNIMPLEMENTED_DEVICE); + + qdev_prop_set_string(dev, "name", name); + qdev_prop_set_uint64(dev, "size", size); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev), 0, base, -900); +} + +static void stm32l467_soc_initfn(Object *obj) +{ + STM32L467State *s = STM32L467_SOC(obj); + + object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M); + + object_initialize_child(obj, "pwr", &s->pwr, TYPE_STM32L476_PWR); + + object_initialize_child(obj, "lptim1", &s->lptim1, TYPE_STM32L476_LPTIM); + + object_initialize_child(obj, "rcc", &s->rcc, TYPE_STM32L476_RCC); + + object_initialize_child(obj, "flash", &s->flash_r, TYPE_STM32L476_FLASH); + + object_initialize_child(obj, "DMA2", &s->dma, TYPE_STM32L476_DMA); + + object_initialize_child(obj, "QSPI", &s->qspi, TYPE_STM32L476_QSPI); + + for (int i = 0; i < 3; i++) { + object_initialize_child(obj, "I2C[*]", &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); + } + + object_initialize_child(obj, "SPI3", &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 */ + + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); + s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0); +} + +static void stm32l467_soc_realize(DeviceState *dev_soc, Error **errp) +{ + STM32L467State *s = STM32L467_SOC(dev_soc); + SysBusDevice *busdev; + + MemoryRegion *system_memory = get_system_memory(); + + /* + * We use s->refclk internally and only define it with qdev_init_clock_in() + * so it is correctly parented and not leaked on an init/deinit; it is not + * intended as an externally exposed clock. + */ + if (clock_has_source(s->refclk)) { + error_setg(errp, "refclk clock must not be wired up by the board code"); + return; + } + + if (!clock_has_source(s->sysclk)) { + error_setg(errp, "sysclk clock must be wired up by the board code"); + return; + } + + /* + * TODO: ideally we should model the SoC RCC and its ability to + * change the sysclk frequency and define different sysclk sources. + */ + + /* The refclk always runs at frequency HCLK / 8 */ + clock_set_mul_div(s->refclk, 8, 1); + clock_set_source(s->refclk, s->sysclk); + + 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_new("stm32l476-syscfg"); + if (!sysbus_realize(SYS_BUS_DEVICE(s->syscfg), errp)) { + return; + } + 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_new(TYPE_STM32L476_GPIO); + s->gpio[i]->id = gpio_desc[i].name; + if (!sysbus_realize(SYS_BUS_DEVICE(s->gpio[i]), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(s->gpio[i]), 0, gpio_desc[i].addr); + } + + 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); + qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); + qdev_connect_clock_in(armv7m, "refclk", s->refclk); + object_property_set_link(OBJECT(&s->armv7m), "memory", + OBJECT(system_memory), &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { + return; + } + + /* PWR registers */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pwr), errp)) { + return; + } + busdev = SYS_BUS_DEVICE(&s->pwr); + sysbus_mmio_map(busdev, 0, PWR_BASE); + + /* LPTIM1 registers */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->lptim1), errp)) { + return; + } + 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 */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->rcc), errp)) { + return; + } + busdev = SYS_BUS_DEVICE(&s->rcc); + sysbus_mmio_map(busdev, 0, RCC_BASE); + + /* FLASH registers */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->flash_r), errp)) { + return; + } + busdev = SYS_BUS_DEVICE(&s->flash_r); + sysbus_mmio_map(busdev, 0, FLASH_R_BASE); + + /* DMA registers */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->dma), errp)) { + return; + } + 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 */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[2]), errp)) { + return; + } + busdev = SYS_BUS_DEVICE(&s->spi[2]); + sysbus_mmio_map(busdev, 0, SPI3_BASE); + + /* QSPI registers */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->qspi), errp)) { + return; + } + 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++) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c[i]), errp)) { + return; + } + } + 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); + + s->exti = qdev_new(TYPE_STM32L476_EXTI); + if (!sysbus_realize(SYS_BUS_DEVICE(s->exti), errp)) { + return; + } + busdev = SYS_BUS_DEVICE(s->exti); + sysbus_mmio_map(busdev, 0, EXTI_BASE); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, EXTI0_IRQn)); + sysbus_connect_irq(busdev, 1, qdev_get_gpio_in(armv7m, EXTI1_IRQn)); + sysbus_connect_irq(busdev, 2, qdev_get_gpio_in(armv7m, EXTI2_IRQn)); + sysbus_connect_irq(busdev, 3, qdev_get_gpio_in(armv7m, EXTI3_IRQn)); + sysbus_connect_irq(busdev, 4, qdev_get_gpio_in(armv7m, EXTI4_IRQn)); + sysbus_connect_irq(busdev, 5, qdev_get_gpio_in(armv7m, EXTI9_5_IRQn)); + sysbus_connect_irq(busdev, 6, qdev_get_gpio_in(armv7m, EXTI15_10_IRQn)); +} + +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) diff --git a/hw/arm/stm32l476_TEMPLATE.c b/hw/arm/stm32l476_TEMPLATE.c new file mode 100644 index 0000000000000..c4119e232c52e --- /dev/null +++ b/hw/arm/stm32l476_TEMPLATE.c @@ -0,0 +1,81 @@ +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/arm/stm32l476_rcc.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.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_dma.c b/hw/arm/stm32l476_dma.c new file mode 100644 index 0000000000000..6b5535d4c66b8 --- /dev/null +++ b/hw/arm/stm32l476_dma.c @@ -0,0 +1,281 @@ +#include "qemu/osdep.h" +#include "hw/arm/stm32l476_dma.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.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..93c66311a5b6d --- /dev/null +++ b/hw/arm/stm32l476_exti.c @@ -0,0 +1,281 @@ +#include "qemu/osdep.h" +#include "hw/arm/stm32l476_exti.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.h" + +static const int EXTI_IMR1 = 0x00; +static const int EXTI_EMR1 = 0x04; +static const int EXTI_RTSR1 = 0x08; +static const int EXTI_FTSR1 = 0x0C; +static const int EXTI_PR1 = 0x14; + +enum { + EXTI0, + EXTI1, + EXTI2, + EXTI3, + EXTI4, + EXTI9_5, + EXTI15_10, + + NUM_INTERRUPT_OUT_LINES +}; + +#define NUM_EVENT_IN_LINES 40 + +typedef struct { + bool IM; + bool EM; + bool RTS; + bool FTS; + bool PIF; + int level; +} Line; + +typedef struct STM32L476ExtiState { + SysBusDevice parent_obj; + MemoryRegion mmio; + Line line[NUM_EVENT_IN_LINES]; + qemu_irq irq[NUM_INTERRUPT_OUT_LINES]; +} STM32L476ExtiState; + +static void stm32l476_exti_reset(DeviceState *dev) +{ + STM32L476ExtiState *s = STM32L476_EXTI(dev); + + for (int i = 0; i < 32; i++) { + s->line[i].IM = false; + s->line[i].EM = false; + s->line[i].RTS = false; + s->line[i].FTS = false; + s->line[i].PIF = false; + } + s->line[17].IM = true; + s->line[23].IM = true; + s->line[24].IM = true; + s->line[25].IM = true; + s->line[26].IM = true; + s->line[27].IM = true; + s->line[28].IM = true; + s->line[29].IM = true; + s->line[30].IM = true; + s->line[31].IM = true; + s->line[32].IM = true; + s->line[33].IM = true; + s->line[34].IM = true; + s->line[39].IM = true; + + // Only applicable for STM32L496xx/4A6xx devices + // s->line[40].IM = true; +} + +static void update_interrupts(STM32L476ExtiState *s) +{ + bool set; + int i; + + qemu_set_irq(s->irq[EXTI0], s->line[0].PIF && s->line[0].IM); + qemu_set_irq(s->irq[EXTI1], s->line[1].PIF && s->line[1].IM); + qemu_set_irq(s->irq[EXTI2], s->line[2].PIF && s->line[2].IM); + qemu_set_irq(s->irq[EXTI3], s->line[3].PIF && s->line[3].IM); + qemu_set_irq(s->irq[EXTI4], s->line[4].PIF && s->line[4].IM); + + set = false; + for (i = 5; i <= 9; i++) { + if (s->line[i].PIF && s->line[i].IM) { + set = true; + } + } + qemu_set_irq(s->irq[EXTI9_5], set); + + set = false; + for (i = 10; i <= 15; i++) { + if (s->line[i].PIF && s->line[i].IM) { + set = true; + } + } + qemu_set_irq(s->irq[EXTI15_10], set); + + for (i = 16; i < NUM_EVENT_IN_LINES; i++) { + if (s->line[i].PIF) { + qemu_log_mask(LOG_GUEST_ERROR, + "[EXTI] Unhandled exti line interrupt: %d\n", i); + } + } +} + +static uint64_t stm32l476_exti_read(void *opaque, hwaddr offset, + unsigned int size) +{ + STM32L476ExtiState *s = opaque; + + switch (offset) { + case EXTI_IMR1: { + uint32_t out = 0; + for (int i = 0; i < 32; i++) { + out |= s->line[i].IM << i; + } + return out; + } + + case EXTI_EMR1: { + uint32_t out = 0; + for (int i = 0; i < 32; i++) { + out |= s->line[i].EM << i; + } + return out; + } + + case EXTI_RTSR1: { + uint32_t out = 0; + for (int i = 0; i < 32; i++) { + out |= s->line[i].RTS << i; + } + return out; + } + + case EXTI_FTSR1: { + uint32_t out = 0; + for (int i = 0; i < 32; i++) { + out |= s->line[i].FTS << i; + } + return out; + } + + case EXTI_PR1: { + uint32_t out = 0; + for (int i = 0; i < 32; i++) { + out |= s->line[i].PIF << i; + } + 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_exti_write(void *opaque, hwaddr offset, uint64_t val64, + unsigned int size) +{ + STM32L476ExtiState *s = opaque; + + switch (offset) { + case EXTI_IMR1: + for (int i = 0; i <= 32; i++) { + s->line[i].IM = extract64(val64, i, 1); + } + break; + case EXTI_EMR1: + for (int i = 0; i <= 32; i++) { + s->line[i].EM = extract64(val64, i, 1); + } + break; + case EXTI_RTSR1: + for (int i = 0; i <= 22; i++) { + if (i == 17) + continue; + s->line[i].RTS = extract64(val64, i, 1); + } + break; + case EXTI_FTSR1: + for (int i = 0; i <= 22; i++) { + if (i == 17) + continue; + s->line[i].FTS = extract64(val64, i, 1); + } + break; + case EXTI_PR1: + for (int i = 0; i <= 22; i++) { + if (i == 17) + continue; + if (val64 & (1U << i)) { + printf("[EXTI] Reset line %d\n", i); + s->line[i].PIF = false; + } + } + 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; + } + + update_interrupts(s); +} + +static const MemoryRegionOps stm32l476_exti_ops = { + .read = stm32l476_exti_read, + .write = stm32l476_exti_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32l476_exti_set_irq(void *opaque, int n, int level) +{ + STM32L476ExtiState *s = opaque; + + Line *x = &s->line[n]; + + if (x->level == level) + return; + + x->level = level; + + /* Rising Edge */ + if (x->RTS && level == 1) { + x->PIF = true; + } + + /* Falling Edge */ + if (x->FTS && level == 0) { + x->PIF = true; + } + + update_interrupts(s); +} + +static void stm32l476_exti_init(Object *obj) +{ + STM32L476ExtiState *s = STM32L476_EXTI(obj); + + for (int i = 0; i < NUM_INTERRUPT_OUT_LINES; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq[i]); + } + + qdev_init_gpio_in(DEVICE(obj), stm32l476_exti_set_irq, NUM_EVENT_IN_LINES); + memory_region_init_io(&s->mmio, obj, &stm32l476_exti_ops, s, + TYPE_STM32L476_EXTI, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} +static void stm32l476_exti_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->reset = stm32l476_exti_reset; +} + +static const TypeInfo stm32l476_exti_info = { + .name = TYPE_STM32L476_EXTI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476ExtiState), + .instance_init = stm32l476_exti_init, + .class_init = stm32l476_exti_class_init, +}; + +static void stm32l476_exti_register_types(void) +{ + type_register_static(&stm32l476_exti_info); +} + +type_init(stm32l476_exti_register_types) diff --git a/hw/arm/stm32l476_flash.c b/hw/arm/stm32l476_flash.c new file mode 100644 index 0000000000000..71255ae272146 --- /dev/null +++ b/hw/arm/stm32l476_flash.c @@ -0,0 +1,108 @@ +#include "qemu/osdep.h" +#include "hw/arm/stm32l476_flash.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.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..e9785c710659e --- /dev/null +++ b/hw/arm/stm32l476_gpio.c @@ -0,0 +1,334 @@ +#include "qemu/osdep.h" +#include "hw/arm/stm32l476_gpio.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.h" + +typedef struct { + uint8_t mode; + uint64_t AFSEL; + uint64_t OT; + uint64_t PUPD; + uint64_t OSPEED; + bool enabled; +} 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; + qemu_irq int_out[16]; +}; + +static void gpio_in(void *opaque, int n, int level) {} + +static bool compare(GpioPin *l, GpioPin *r) +{ + if (l->mode != r->mode) + return false; + if (l->AFSEL != r->AFSEL) + return false; + if (l->PUPD != r->PUPD) + return false; + if (l->OT != r->OT) + return false; + if (l->OSPEED != r->OSPEED) + return false; + + return true; +} + +static void set_gpio(STM32L476GpioState *PState, int I, bool B); + +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_gpio(s, i, true); + } else if (extract64(val64, 16 + i, 1)) { + set_gpio(s, i, false); + } + } + 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 */ + set_gpio(s, i, false); + } + } + 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 (compare(&backup[i], &s->pins[i])) { + continue; + } + + printf("[%s] Pin %d changed to ", DEVICE(s)->id, i); + + if (false) { + 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 void set_gpio(STM32L476GpioState *s, int i, bool enabled) +{ + if (s->pins[i].mode != 0b01) + return; + + if (s->pins[i].enabled != enabled) { + bool print = true; + if (strcmp(DEVICE(s)->id, "GPIOG") == 0) { + switch (i) { + case 12: + case 14: + print = false; + break; + } + } else if (strcmp(DEVICE(s)->id, "GPIOA") == 0) { + switch (i) { + case 4: + print = false; + break; + } + } + + if (print) { + printf("[%s] Pin %d set to %d\n", DEVICE(s)->id, i, enabled); + } + s->pins[i].enabled = enabled; + qemu_set_irq(s->int_out[i], enabled); + } +} + +static const MemoryRegionOps stm32l476_gpio_ops = { + .read = stm32l476_gpio_read, + .write = stm32l476_gpio_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32l476_gpio_init(DeviceState *dev, Error **pError) +{ + STM32L476GpioState *s = STM32L476_GPIO(dev); + qdev_init_gpio_out(dev, s->int_out, 16); + qdev_init_gpio_in(dev, gpio_in, 16); + + memory_region_init_io(&s->mmio, dev, &stm32l476_gpio_ops, s, + TYPE_STM32L476_GPIO, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); +} + +static void stm32l476_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_gpio_reset; + dc->realize = stm32l476_gpio_init; +} + +static const TypeInfo stm32l476_gpio_info = { + .name = TYPE_STM32L476_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476GpioState), + .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..afa3178c1ed80 --- /dev/null +++ b/hw/arm/stm32l476_i2c.c @@ -0,0 +1,379 @@ +#include "qemu/osdep.h" +#include "hw/arm/stm32l476_i2c.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/i2c/i2c.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "sysemu/sysemu.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_ICR 0x1C +#define I2C_PECR 0x20 +#define I2C_RXDR 0x24 +#define I2C_TXDR 0x28 + +static void stm32l476_i2c_reset(DeviceState *dev) +{ + STM32L476I2CState *s = STM32L476_I2C(dev); + + // I2C_CR1 + s->PE = 0; + s->TXIE = 0; + s->RXIE = 0; + s->ADDRIE = 0; + s->NACKIE = 0; + s->STOPIE = 0; + s->TCIE = 0; + s->ERRIE = 0; + s->DNF = 0; + s->ANFOFF = 0; + s->TXDMAEN = 0; + s->RXDMAEN = 0; + s->SBC = 0; + s->NOSTRETCH = 0; + s->WUPEN = 0; + s->GCEN = 0; + s->SMBHEN = 0; + s->SMBDEN = 0; + s->ALERTEN = 0; + s->PECEN = 0; + + // I2C_CR2 + s->SADD = 0; + s->RD_WRN = 0; + s->ADD10 = 0; + s->HEAD10R = 0; + s->START = 0; + s->STOP = 0; + s->NACK = 0; + s->NBYTES = 0; + s->RELOAD = 0; + s->AUTOEND = 0; + s->PECBYTE = 0; +} + +static uint64_t stm32l476_i2c_read(void *opaque, hwaddr offset, + unsigned int size) +{ + STM32L476I2CState *s = opaque; + + switch (offset) { + case I2C_CR1: { + uint32_t out = 0; + out |= s->PE << 0; + out |= s->TXIE << 1; + out |= s->RXIE << 2; + out |= s->ADDRIE << 3; + out |= s->NACKIE << 4; + out |= s->STOPIE << 5; + out |= s->TCIE << 6; + out |= s->ERRIE << 7; + out |= s->DNF << 8; + out |= s->ANFOFF << 12; + out |= s->TXDMAEN << 14; + out |= s->RXDMAEN << 15; + out |= s->SBC << 16; + out |= s->NOSTRETCH << 17; + out |= s->WUPEN << 18; + out |= s->GCEN << 19; + out |= s->SMBHEN << 20; + out |= s->SMBDEN << 21; + out |= s->ALERTEN << 22; + out |= s->PECEN << 23; + return out; + } + case I2C_CR2: { + printf("[%s] Read CR2\n", s->name); + 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: { + s->RXNE = !fifo8_is_empty(&s->fifo); + printf("[%s] Read ISR\n", s->name); + uint32_t out = 0; + out |= s->TXIS << 1; + out |= s->RXNE << 2; + out |= s->ADDR << 3; + out |= s->NACKF << 4; + out |= s->STOPF << 5; + out |= s->TC << 6; + out |= s->BUSY << 15; + return out; + } + + case I2C_RXDR: + { + uint8_t i = fifo8_pop(&s->fifo); + printf("[%s] READ 0x%02X\n", s->name, i); + if(fifo8_is_empty(&s->fifo) && s->AUTOEND) { + s->STOPF = TRUE; + s->BUSY = FALSE; + } + return i; + } + + case I2C_OAR1: + case I2C_OAR2: + 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_CR1: { + s->PE = extract64(val64, 0, 1); + s->TXIE = extract64(val64, 1, 1); + s->RXIE = extract64(val64, 2, 1); + s->ADDRIE = extract64(val64, 3, 1); + s->NACKIE = extract64(val64, 4, 1); + s->STOPIE = extract64(val64, 5, 1); + s->TCIE = extract64(val64, 6, 1); + s->ERRIE = extract64(val64, 7, 1); + s->DNF = extract64(val64, 8, 4); + s->ANFOFF = extract64(val64, 12, 1); + s->TXDMAEN = extract64(val64, 14, 1); + s->RXDMAEN = extract64(val64, 15, 1); + s->SBC = extract64(val64, 16, 1); + s->NOSTRETCH = extract64(val64, 17, 1); + s->WUPEN = extract64(val64, 18, 1); + s->GCEN = extract64(val64, 19, 1); + s->SMBHEN = extract64(val64, 20, 1); + s->SMBDEN = extract64(val64, 21, 1); + s->ALERTEN = extract64(val64, 22, 1); + s->PECEN = extract64(val64, 23, 1); + + assert(!s->TXIE); + assert(!s->RXIE); + assert(!s->ADDRIE); + assert(!s->NACKIE); + assert(!s->STOPIE); + assert(!s->TCIE); + assert(!s->ERRIE); + break; + } + + 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->STOP) { + s->TC = 0; + } + + if (s->START) { + printf("[%s] START (%s %d bytes 0x%02X)\n", s->name, + s->RD_WRN == 0 ? "write" : "read", s->NBYTES, s->SADD >> 1); + if (s->BUSY) { + assert(i2c_bus_busy(s->bus)); + } + + s->BUSY = true; + int err = i2c_start_transfer(s->bus, (s->SADD & 0b0011111110) >> 1, + s->RD_WRN == 1); + s->START = false; + if (err) { + printf("[%s] Error starting transfer\n", s->name); + // No matching device on bus + s->NACKF = true; + s->STOPF = true; + s->BUSY = false; + } else { + + if (s->RD_WRN == 1) { + assert(s->NBYTES > 0); + while (s->NBYTES > 0) { + s->NBYTES--; + fifo8_push(&s->fifo, i2c_recv(s->bus)); + } + + if (s->AUTOEND) { + i2c_end_transfer(s->bus); + s->STOPF = TRUE; + } + } + s->TXIS = true; + } + } + break; + } + + case I2C_TXDR: { + printf("[%s] Send data to 0x%X: 0x%02X\n", s->name, + (s->SADD & 0b0011111110) >> 1, val64); + s->NBYTES--; + if (s->RELOAD == 0 && s->NBYTES == 0) { + if (s->AUTOEND) { + i2c_end_transfer(s->bus); + s->STOPF = true; + s->BUSY = FALSE; + } else { + s->TC = TRUE; + } + } + i2c_send(s->bus, val64 & 0xFF); + break; + } + + case I2C_ICR: { + printf("[%s] ICR: 0x%08X\n", s->name, val64); + if (extract64(val64, 3, 1)) { + s->ADDR = false; + s->START = false; + } + if (extract64(val64, 4, 1)) { + s->NACKF = false; + } + if (extract64(val64, 5, 1)) { + s->STOPF = false; + } + + if (extract64(val64, 8, 1)) { + s->BERR = false; + } + if (extract64(val64, 9, 1)) { + s->ARLO = false; + } + if (extract64(val64, 10, 1)) { + s->OVR = false; + } + if (extract64(val64, 11, 1)) { + // If the SMBus feature is not supported, this bit is reserved and + // forced by hardware to ‘0’ + s->PECERR = false; + } + if (extract64(val64, 12, 1)) { + // If the SMBus feature is not supported, this bit is reserved and + // forced by hardware to ‘0’ + s->TIMEOUT = false; + } + if (extract64(val64, 13, 1)) { + // If the SMBus feature is not supported, this bit is reserved and + // forced by hardware to ‘0’ + s->ALERT = false; + } + break; + } + + case I2C_OAR1: + case I2C_OAR2: + case I2C_TIMINGR: + case I2C_PECR: + 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"); + fifo8_create(&s->fifo, 256); +} + +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..57fdcf3102c01 --- /dev/null +++ b/hw/arm/stm32l476_lptim.c @@ -0,0 +1,287 @@ +#include "qemu/osdep.h" +#include "hw/arm/stm32l476_lptim.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.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) +{ + if (false) { + 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: { + if (false) { + 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; + if (false) { + 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"); + if (false) { + 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); + if (false) { + 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..63b46248cda51 --- /dev/null +++ b/hw/arm/stm32l476_pwr.c @@ -0,0 +1,130 @@ +#include "qemu/osdep.h" +#include "hw/arm/stm32l476_pwr.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.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..5d10ae2d53fb6 --- /dev/null +++ b/hw/arm/stm32l476_qspi.c @@ -0,0 +1,472 @@ +#include "qemu/osdep.h" +#include "hw/arm/stm32l476_qspi.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.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..fff270cdc74a1 --- /dev/null +++ b/hw/arm/stm32l476_rcc.c @@ -0,0 +1,174 @@ +#include "qemu/osdep.h" +#include "hw/arm/stm32l476_rcc.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.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..bb14a7239b970 --- /dev/null +++ b/hw/arm/stm32l476_spi.c @@ -0,0 +1,141 @@ +#include "qemu/osdep.h" +#include "hw/arm/stm32l476_spi.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/ssi/ssi.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.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..208810aa64a8a --- /dev/null +++ b/hw/arm/stm32l476_syscfg.c @@ -0,0 +1,132 @@ +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.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/arm/stm32l476_tim.c b/hw/arm/stm32l476_tim.c new file mode 100644 index 0000000000000..cc3d6ebd87fcd --- /dev/null +++ b/hw/arm/stm32l476_tim.c @@ -0,0 +1,87 @@ +#include "qemu/osdep.h" +#include "hw/arm/stm32l476_tim.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.h" + +#define TIMx_CR1 0x00 +#define TIMx_EGR 0x14 +#define TIMx_CNT 0x24 +#define TIMx_PSC 0x28 +#define TIMx_ARR 0x2C + +static void stm32l476_tim_reset(DeviceState *dev) +{ + STM32L476TimState *s = STM32L476_TIM(dev); +} + +static uint64_t stm32l476_tim_read(void *opaque, hwaddr offset, + unsigned int size) +{ + STM32L476TimState *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_tim_write(void *opaque, hwaddr offset, uint64_t val64, + unsigned int size) +{ + STM32L476TimState *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_tim_ops = { + .read = stm32l476_tim_read, + .write = stm32l476_tim_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32l476_tim_init(Object *obj) +{ + STM32L476TimState *s = STM32L476_TIM(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l476_tim_ops, s, + TYPE_STM32L476_TIM, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void stm32l476_tim_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32l476_tim_reset; +} + +static const TypeInfo stm32l476_tim_info = { + .name = TYPE_STM32L476_TIM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L476TimState), + .instance_init = stm32l476_tim_init, + .class_init = stm32l476_tim_class_init, +}; + +static void stm32l476_tim_register_types(void) +{ + type_register_static(&stm32l476_tim_info); +} + +type_init(stm32l476_tim_register_types) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index b77503dc845f5..b6c5be0514098 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -333,6 +333,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) }, @@ -756,6 +757,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/jdi-lpm013m126c.c b/hw/display/jdi-lpm013m126c.c new file mode 100644 index 0000000000000..f655091a854b9 --- /dev/null +++ b/hw/display/jdi-lpm013m126c.c @@ -0,0 +1,228 @@ +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/ssi/ssi.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "sysemu/sysemu.h" +#include "ui/console.h" +#include "ui/pixel_ops.h" + +typedef struct { + uint8_t r; + uint8_t g; + uint8_t b; +} rgb; + +typedef struct { + struct SSIPeripheral 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; +} + +static 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; +} + +static uint32_t jdi_lpm013m126c_transfer(SSIPeripheral *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(struct SSIPeripheral *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(SSIPeripheral *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) +{ + SSIPeripheralClass *k = SSI_PERIPHERAL_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_PERIPHERAL, + .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/display/meson.build b/hw/display/meson.build index 861c43ff98471..f11a0e24f3dd0 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -98,3 +98,5 @@ endif specific_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_lcdc.c')) modules += { 'hw-display': hw_display_modules } + +softmmu_ss.add(files('jdi-lpm013m126c.c')) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 13df002ce4dbc..c30da70b86f56 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2683,7 +2683,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; /* * This device provides a single memory region which covers the diff --git a/include/hw/arm/stm32f405_soc.h b/include/hw/arm/stm32f405_soc.h index 5bb0c8d569791..dcada45f7c920 100644 --- a/include/hw/arm/stm32f405_soc.h +++ b/include/hw/arm/stm32f405_soc.h @@ -25,14 +25,14 @@ #ifndef HW_ARM_STM32F405_SOC_H #define HW_ARM_STM32F405_SOC_H -#include "hw/misc/stm32f4xx_syscfg.h" -#include "hw/timer/stm32f2xx_timer.h" -#include "hw/char/stm32f2xx_usart.h" #include "hw/adc/stm32f2xx_adc.h" +#include "hw/arm/armv7m.h" +#include "hw/char/stm32f2xx_usart.h" #include "hw/misc/stm32f4xx_exti.h" +#include "hw/misc/stm32f4xx_syscfg.h" #include "hw/or-irq.h" #include "hw/ssi/stm32f2xx_spi.h" -#include "hw/arm/armv7m.h" +#include "hw/timer/stm32f2xx_timer.h" #include "qom/object.h" #define TYPE_STM32F405_SOC "stm32f405-soc" diff --git a/include/hw/arm/stm32l467_soc.h b/include/hw/arm/stm32l467_soc.h new file mode 100644 index 0000000000000..5ddca2e046265 --- /dev/null +++ b/include/hw/arm/stm32l467_soc.h @@ -0,0 +1,48 @@ +#ifndef HW_ARM_STM32L467_SOC_H +#define HW_ARM_STM32L467_SOC_H + +#include "hw/arm/armv7m.h" +#include "hw/arm/stm32l476_dma.h" +#include "hw/arm/stm32l476_flash.h" +#include "hw/arm/stm32l476_i2c.h" +#include "hw/arm/stm32l476_lptim.h" +#include "hw/arm/stm32l476_pwr.h" +#include "hw/arm/stm32l476_qspi.h" +#include "hw/arm/stm32l476_rcc.h" +#include "hw/arm/stm32l476_spi.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; + DeviceState *exti; + + Clock *sysclk; + Clock *refclk; +} STM32L467State; + +#endif diff --git a/include/hw/arm/stm32l476_dma.h b/include/hw/arm/stm32l476_dma.h new file mode 100644 index 0000000000000..644960d7ffd31 --- /dev/null +++ b/include/hw/arm/stm32l476_dma.h @@ -0,0 +1,47 @@ +#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_exti.h b/include/hw/arm/stm32l476_exti.h new file mode 100644 index 0000000000000..77429ffcd6439 --- /dev/null +++ b/include/hw/arm/stm32l476_exti.h @@ -0,0 +1,8 @@ +#ifndef HW_ARM_STM32L476_EXTI_H +#define HW_ARM_STM32L476_EXTI_H + +#define TYPE_STM32L476_EXTI "stm32l476-exti" +#define STM32L476_EXTI(obj) \ + OBJECT_CHECK(STM32L476ExtiState, (obj), TYPE_STM32L476_EXTI) + +#endif diff --git a/include/hw/arm/stm32l476_flash.h b/include/hw/arm/stm32l476_flash.h new file mode 100644 index 0000000000000..cac63f9e390d3 --- /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..9f8ff2c1f97b8 --- /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..3e2c620951d61 --- /dev/null +++ b/include/hw/arm/stm32l476_i2c.h @@ -0,0 +1,76 @@ +#ifndef HW_ARM_STM32L476_I2C_H +#define HW_ARM_STM32L476_I2C_H + +#include "hw/arm/armv7m.h" +#include "qemu/fifo8.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_CR1 + bool PE; + bool TXIE; + bool RXIE; + bool ADDRIE; + bool NACKIE; + bool STOPIE; + bool TCIE; + bool ERRIE; + uint8_t DNF; + bool ANFOFF; + bool TXDMAEN; + bool RXDMAEN; + bool SBC; + bool NOSTRETCH; + bool WUPEN; + bool GCEN; + bool SMBHEN; + bool SMBDEN; + bool ALERTEN; + bool PECEN; + + // I2C_CR2 + uint64_t SADD; + uint8_t RD_WRN; + uint64_t ADD10; + uint64_t HEAD10R; + bool START; + bool STOP; + bool NACK; + uint64_t NBYTES; + uint64_t RELOAD; + bool AUTOEND; + bool PECBYTE; + + // I2C_ISR + bool TXE; + bool TXIS; + bool RXNE; + bool ADDR; + bool NACKF; + bool STOPF; + bool TC; + bool TCR; + bool BERR; + bool ARLO; + bool OVR; + bool PECERR; + bool TIMEOUT; + bool ALERT; + bool BUSY; + + I2CBus *bus; + Fifo8 fifo; +} STM32L476I2CState; + +#endif diff --git a/include/hw/arm/stm32l476_lptim.h b/include/hw/arm/stm32l476_lptim.h new file mode 100644 index 0000000000000..3a0c64deab5dd --- /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..5cc08fd16a5a5 --- /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..3487e9cd30eb9 --- /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/ssi/ssi.h" +#include "hw/sysbus.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..ec579e2bc57f2 --- /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..66bfe67210c4d --- /dev/null +++ b/include/hw/arm/stm32l476_spi.h @@ -0,0 +1,26 @@ +#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/include/hw/arm/stm32l476_tim.h b/include/hw/arm/stm32l476_tim.h new file mode 100644 index 0000000000000..8d2b4a6dc5d12 --- /dev/null +++ b/include/hw/arm/stm32l476_tim.h @@ -0,0 +1,20 @@ +#ifndef HW_ARM_STM32L476_LPTIM_H +#define HW_ARM_STM32L476_LPTIM_H + +#include "hw/arm/armv7m.h" + +#define TYPE_STM32L476_TIM "stm32l476-tim" +#define STM32L476_TIM(obj) \ + OBJECT_CHECK(STM32L476TimState, (obj), TYPE_STM32L476_TIM) + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + qemu_irq irq; + +} STM32L476TimState; + +#endif \ No newline at end of file diff --git a/sram1.bin b/sram1.bin new file mode 100644 index 0000000000000..de5a1e51d0f98 Binary files /dev/null and b/sram1.bin differ diff --git a/sram2.bin b/sram2.bin new file mode 100644 index 0000000000000..ac86eac7b5333 Binary files /dev/null and b/sram2.bin differ diff --git a/sramphp.bin b/sramphp.bin new file mode 100644 index 0000000000000..739f31eef84df Binary files /dev/null and b/sramphp.bin differ