[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] hw/arm/stm32f405: Add preliminary RCC emulation support
From: |
Alistair Francis |
Subject: |
Re: [PATCH] hw/arm/stm32f405: Add preliminary RCC emulation support |
Date: |
Fri, 6 Mar 2020 18:13:29 -0800 |
On Thu, Mar 5, 2020 at 5:06 PM Stephanos Ioannidis <address@hidden> wrote:
>
> > These should at least be numbers (if not macros) instead of binary.
>
> Just wanted to follow the convention that the reference manual uses (it lists
> the field values in binary).
The one I read was both, in this case I think the binary form is more
complex then just the decimal values.
>
> > Can't you just do (rcc_pllcfgr.pllp + 1) * 2 instead of a switch case?
> > I don't think we need a switch statement here
> > if (rcc_cfgr.hpre) <= 8) {
> > ahb_div = 1;
> > } else {
> > ahb_div = (rcc_cfgr.hpre - 7) * 2
> > }
>
> I wanted to be as verbose as possible and leave any optimisation up to the
> compiler. After all, when reading code, the switch implementation is a direct
> representation of the value mapping on and is therefore much easier to
> understand- given that it is not excessively long.
Fair, this one isn't too bad.
>
> > Where does this come from? I can't seem to find it in the RM.
>
> This is an equation representation of the clock tree diagram (refer to the
> "Figure 21. Clock tree" from RM0900).
>
> > Shouldn't this be 0x0477 7F33?
>
> No, that is for STM32F42xxx and STM32F43xxx. See "7.3.19 RCC APB2 peripheral
> clock enabled in low power mode register (RCC_APB2LPENR)" from RM0900.
Ah, I opened the wrong one then.
>
> > Shouldn't this be PLLSAIRDYF?
> > Aren't you missing a PLLSAIRDYIE
>
> No, PLLSAIRDYF and PLLSAIRDYIE are for STM32F42xxx and STM32F43xxx. STM32F405
> does not have these fields.
Same as above.
Alistair
>
> Stephanos
>
> -----Original Message-----
> From: Alistair Francis <address@hidden>
> Sent: Friday, March 6, 2020 4:40 AM
> To: Stephanos Ioannidis <address@hidden>
> Cc: Peter Maydell <address@hidden>; Alistair Francis <address@hidden>; open
> list:All patches CC here <address@hidden>; open list:ARM TCG CPUs
> <address@hidden>
> Subject: Re: [PATCH] hw/arm/stm32f405: Add preliminary RCC emulation support
>
> = heOn Sat, Feb 29, 2020 at 6:12 AM Stephanos Ioannidis <address@hidden>
> wrote:
> >
> > The RCC (reset and clock control) is a hardware peripheral reset and
> > clock configuration controller available on the STM32F4xx series
> > devices.
> >
> > This commit adds preliminary support for the RCC peripheral emulation,
> > in order to support proper emulation of the firmware images that use
> > the STM32Cube driver, which configures and validates the RCC registers
> > during system initialisation.
> >
> > In addition, the current STM32F405 SoC implementation does not specify
> > the Cortex-M SysTick clock scaling factor and this causes the QEMU to
> > become unresponsive as soon as the SysTick timer is enabled by the
> > guest. This problem is addressed by configuring the SysTick clock
> > scaling factor from the AHB clock frequency computed using the RCC
> > clock configurations.
> >
> > Signed-off-by: Stephanos Ioannidis <address@hidden>
> > ---
> > hw/arm/Kconfig | 1 +
> > hw/arm/netduinoplus2.c | 1 +
> > hw/arm/stm32f405_soc.c | 17 +-
> > hw/misc/Kconfig | 3 +
> > hw/misc/Makefile.objs | 1 +
> > hw/misc/stm32f4xx_rcc.c | 472 ++++++++++++++++++++++++++++++++
> > hw/misc/trace-events | 4 +
> > include/hw/arm/stm32f405_soc.h | 3 +
> > include/hw/misc/stm32f4xx_rcc.h | 316 +++++++++++++++++++++
> > 9 files changed, 817 insertions(+), 1 deletion(-) create mode 100644
> > hw/misc/stm32f4xx_rcc.c create mode 100644
> > include/hw/misc/stm32f4xx_rcc.h
> >
> > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index
> > 3d86691ae0..16442b3c4b 100644
> > --- a/hw/arm/Kconfig
> > +++ b/hw/arm/Kconfig
> > @@ -314,6 +314,7 @@ config STM32F205_SOC config STM32F405_SOC
> > bool
> > select ARM_V7M
> > + select STM32F4XX_RCC
> > select STM32F4XX_SYSCFG
> > select STM32F4XX_EXTI
> >
> > diff --git a/hw/arm/netduinoplus2.c b/hw/arm/netduinoplus2.c index
> > e5e247edbe..37d57dafe4 100644
> > --- a/hw/arm/netduinoplus2.c
> > +++ b/hw/arm/netduinoplus2.c
> > @@ -36,6 +36,7 @@ static void netduinoplus2_init(MachineState
> > *machine)
> >
> > dev = qdev_create(NULL, TYPE_STM32F405_SOC);
> > qdev_prop_set_string(dev, "cpu-type",
> > ARM_CPU_TYPE_NAME("cortex-m4"));
> > + qdev_prop_set_uint32(dev, "hse-frequency", 25000000);
> > object_property_set_bool(OBJECT(dev), true, "realized",
> > &error_fatal);
> >
> > armv7m_load_kernel(ARM_CPU(first_cpu),
> > diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c index
> > 9bcad97853..5abbbc96c0 100644
> > --- a/hw/arm/stm32f405_soc.c
> > +++ b/hw/arm/stm32f405_soc.c
> > @@ -30,6 +30,7 @@
> > #include "hw/arm/stm32f405_soc.h"
> > #include "hw/misc/unimp.h"
> >
> > +#define RCC_ADDR 0x40023800
> > #define SYSCFG_ADD 0x40013800
> > static const uint32_t usart_addr[] = { 0x40011000, 0x40004400, 0x40004800,
> > 0x40004C00, 0x40005000,
> > 0x40011400, @@ -59,6 +60,9 @@ static void stm32f405_soc_initfn(Object *obj)
> > sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m),
> > TYPE_ARMV7M);
> >
> > + sysbus_init_child_obj(obj, "rcc", &s->rcc, sizeof(s->rcc),
> > + TYPE_STM32F4XX_RCC);
> > +
> > sysbus_init_child_obj(obj, "syscfg", &s->syscfg, sizeof(s->syscfg),
> > TYPE_STM32F4XX_SYSCFG);
> >
> > @@ -130,6 +134,17 @@ static void stm32f405_soc_realize(DeviceState
> > *dev_soc, Error **errp)
> > return;
> > }
> >
> > + /* Reset and clock control */
> > + dev = DEVICE(&s->rcc);
> > + qdev_prop_set_uint32(dev, "hse-frequency", s->hse_frequency);
> > + object_property_set_bool(OBJECT(&s->rcc), true, "realized", &err);
> > + if (err != NULL) {
> > + error_propagate(errp, err);
> > + return;
> > + }
> > + busdev = SYS_BUS_DEVICE(dev);
> > + sysbus_mmio_map(busdev, 0, RCC_ADDR);
> > +
> > /* System configuration controller */
> > dev = DEVICE(&s->syscfg);
> > object_property_set_bool(OBJECT(&s->syscfg), true, "realized",
> > &err); @@ -260,7 +275,6 @@ static void stm32f405_soc_realize(DeviceState
> > *dev_soc, Error **errp)
> > create_unimplemented_device("GPIOH", 0x40021C00, 0x400);
> > create_unimplemented_device("GPIOI", 0x40022000, 0x400);
> > create_unimplemented_device("CRC", 0x40023000, 0x400);
> > - create_unimplemented_device("RCC", 0x40023800, 0x400);
> > create_unimplemented_device("Flash Int", 0x40023C00, 0x400);
> > create_unimplemented_device("BKPSRAM", 0x40024000, 0x400);
> > create_unimplemented_device("DMA1", 0x40026000, 0x400);
> > @@ -274,6 +288,7 @@ static void stm32f405_soc_realize(DeviceState
> > *dev_soc, Error **errp)
> >
> > static Property stm32f405_soc_properties[] = {
> > DEFINE_PROP_STRING("cpu-type", STM32F405State, cpu_type),
> > + DEFINE_PROP_UINT32("hse-frequency", STM32F405State,
> > + hse_frequency, 0),
> > DEFINE_PROP_END_OF_LIST(),
> > };
> >
> > diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index
> > bdd77d8020..0cb5f2af68 100644
> > --- a/hw/misc/Kconfig
> > +++ b/hw/misc/Kconfig
> > @@ -79,6 +79,9 @@ config IMX
> > select SSI
> > select USB_EHCI_SYSBUS
> >
> > +config STM32F4XX_RCC
> > + bool
> > +
> > config STM32F2XX_SYSCFG
> > bool
> >
> > diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index
> > da993f45b7..58a856c00d 100644
> > --- a/hw/misc/Makefile.objs
> > +++ b/hw/misc/Makefile.objs
> > @@ -58,6 +58,7 @@ common-obj-$(CONFIG_RASPI) += bcm2835_thermal.o
> > common-obj-$(CONFIG_SLAVIO) += slavio_misc.o
> > common-obj-$(CONFIG_ZYNQ) += zynq_slcr.o
> > common-obj-$(CONFIG_ZYNQ) += zynq-xadc.o
> > +common-obj-$(CONFIG_STM32F4XX_RCC) += stm32f4xx_rcc.o
> > common-obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
> > common-obj-$(CONFIG_STM32F4XX_SYSCFG) += stm32f4xx_syscfg.o
> > common-obj-$(CONFIG_STM32F4XX_EXTI) += stm32f4xx_exti.o diff --git
> > a/hw/misc/stm32f4xx_rcc.c b/hw/misc/stm32f4xx_rcc.c new file mode
> > 100644 index 0000000000..297f2430b8
> > --- /dev/null
> > +++ b/hw/misc/stm32f4xx_rcc.c
> > @@ -0,0 +1,472 @@
> > +/*
> > + * STM32F4xx RCC
> > + *
> > + * Copyright (c) 2020 Stephanos Ioannidis <address@hidden>
> > + *
> > + * Permission is hereby granted, free of charge, to any person
> > +obtaining a copy
> > + * of this software and associated documentation files (the
> > +"Software"), to deal
> > + * in the Software without restriction, including without limitation
> > +the rights
> > + * to use, copy, modify, merge, publish, distribute, sublicense,
> > +and/or sell
> > + * copies of the Software, and to permit persons to whom the Software
> > +is
> > + * furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice shall be
> > +included in
> > + * all copies or substantial portions of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> > +EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > +MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
> > +SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
> > +OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> > +ARISING FROM,
> > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> > +DEALINGS IN
> > + * THE SOFTWARE.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qemu/log.h"
> > +#include "trace.h"
> > +#include "hw/irq.h"
> > +#include "hw/qdev-properties.h"
> > +#include "migration/vmstate.h"
> > +#include "hw/misc/stm32f4xx_rcc.h"
> > +#include "hw/timer/armv7m_systick.h"
> > +
> > +#define HSI_FREQ (16000000)
> > +
> > +static void rcc_update_clock(STM32F4xxRccState *s) {
> > + uint32_t pll_src, pll_prediv, pll_mult, pll_postdiv, pll_freq;
> > + uint32_t ahb_div, ahb_freq;
> > + uint32_t sysclk_freq;
> > + uint32_t systick_freq;
> > +
> > + /* Resolve PLL clock source */
> > + pll_src = s->rcc_pllcfgr.pllsrc ? s->hse_frequency : HSI_FREQ;
> > +
> > + /* Resolve PLL input division factor */
> > + switch (s->rcc_pllcfgr.pllm) {
> > + case 0b000010 ... 0b111111:
> > + pll_prediv = s->rcc_pllcfgr.pllm;
> > + break;
> > + default:
> > + qemu_log_mask(
> > + LOG_GUEST_ERROR,
> > + "%s: Invalid PLLM\n", __func__);
> > + return;
> > + }
> > +
> > + /* Resolve PLL VCO multiplication factor */
> > + switch (s->rcc_pllcfgr.plln) {
> > + case 0b000110010 ... 0b110110000:
>
> These should at least be numbers (if not macros) instead of binary.
>
> > + pll_mult = s->rcc_pllcfgr.plln;
> > + break;
> > + default:
> > + qemu_log_mask(
> > + LOG_GUEST_ERROR,
> > + "%s: Invalid PLLN\n", __func__);
> > + return;
> > + }
> > +
> > + /* Resolve PLL output division factor */
> > + switch (s->rcc_pllcfgr.pllp) {
> > + case 0b00:
> > + pll_postdiv = 2;
> > + break;
> > + case 0b01:
> > + pll_postdiv = 4;
> > + break;
> > + case 0b10:
> > + pll_postdiv = 6;
> > + break;
> > + case 0b11:
> > + pll_postdiv = 8;
> > + break;
> > + }
>
> Can't you just do (rcc_pllcfgr.pllp + 1) * 2 instead of a switch case?
>
> > +
> > + /* Compute PLL output frequency */
> > + pll_freq = pll_src / pll_prediv * pll_mult / pll_postdiv;
>
> Where does this come from? I can't seem to find it in the RM.
>
> > +
> > + /* Resolve SYSCLK frequency */
> > + switch (s->rcc_cfgr.sw) {
> > + case 0b00: /* High-speed internal oscillator (fixed at 16MHz) */
> > + sysclk_freq = HSI_FREQ;
> > + break;
> > + case 0b01: /* High-speed external oscillator */
> > + sysclk_freq = s->hse_frequency;
> > + break;
> > + case 0b10: /* PLL */
> > + sysclk_freq = pll_freq;
> > + break;
> > + default:
> > + qemu_log_mask(
> > + LOG_GUEST_ERROR,
> > + "%s: Invalid system clock source selected\n", __func__);
> > + return;
> > + }
> > +
> > + /* Resolve AHB prescaler division ratio */
> > + switch (s->rcc_cfgr.hpre) {
> > + case 0b0000 ... 0b0111:
> > + ahb_div = 1;
> > + break;
> > + case 0b1000:
> > + ahb_div = 2;
> > + break;
> > + case 0b1001:
> > + ahb_div = 4;
> > + break;
> > + case 0b1010:
> > + ahb_div = 8;
> > + break;
> > + case 0b1011:
> > + ahb_div = 16;
> > + break;
> > + case 0b1100:
> > + ahb_div = 64;
> > + break;
> > + case 0b1101:
> > + ahb_div = 128;
> > + break;
> > + case 0b1110:
> > + ahb_div = 256;
> > + break;
> > + case 0b1111:
> > + ahb_div = 512;
> > + break;
> > + }
>
> I don't think we need a switch statement here
>
> if (rcc_cfgr.hpre) <= 8) {
> ahb_div = 1;
> } else {
> ahb_div = (rcc_cfgr.hpre - 7) * 2
> }
>
> > +
> > + /* Compute AHB frequency */
> > + ahb_freq = sysclk_freq / ahb_div;
> > +
> > + /* Compute and set Cortex-M SysTick timer clock frequency */
> > + systick_freq = ahb_freq / 8;
> > + system_clock_scale = 1000000000 / systick_freq; }
> > +
> > +static void stm32f4xx_rcc_reset(DeviceState *dev) {
> > + STM32F4xxRccState *s = STM32F4XX_RCC(dev);
> > +
> > + /* Initialise register values */
> > + s->rcc_cr.reg = 0x00000083;
> > + s->rcc_pllcfgr.reg = 0x24003010;
> > + s->rcc_cfgr.reg = 0x00000000;
> > + s->rcc_cir.reg = 0x00000000;
> > + s->rcc_ahb1rstr.reg = 0x00000000;
> > + s->rcc_ahb2rstr.reg = 0x00000000;
> > + s->rcc_ahb3rstr.reg = 0x00000000;
> > + s->rcc_apb1rstr.reg = 0x00000000;
> > + s->rcc_apb2rstr.reg = 0x00000000;
> > + s->rcc_ahb1enr.reg = 0x00100000;
> > + s->rcc_ahb2enr.reg = 0x00000000;
> > + s->rcc_ahb3enr.reg = 0x00000000;
> > + s->rcc_apb1enr.reg = 0x00000000;
> > + s->rcc_apb2enr.reg = 0x00000000;
> > + s->rcc_ahb1lpenr.reg = 0x7E6791FF;
> > + s->rcc_ahb2lpenr.reg = 0x000000F1;
> > + s->rcc_ahb3lpenr.reg = 0x00000001;
> > + s->rcc_apb1lpenr.reg = 0x36FEC9FF;
> > + s->rcc_apb2lpenr.reg = 0x00075F33;
>
> Shouldn't this be 0x0477 7F33?
>
> > + s->rcc_bdcr.reg = 0x00000000;
> > + s->rcc_csr.reg = 0x0E000000;
> > + s->rcc_sscgr.reg = 0x00000000;
> > + s->rcc_plli2scfgr.reg = 0x20003000;
> > +
> > + /* Update clock based on the reset state */
> > + rcc_update_clock(s);
> > +}
> > +
> > +static void rcc_cr_write(STM32F4xxRccState *s, RccCrType val) {
> > + /* Set internal high-speed clock state */
> > + s->rcc_cr.hsirdy = s->rcc_cr.hsion = val.hsion;
> > + /* Set external high-speed clock state */
> > + s->rcc_cr.hserdy = s->rcc_cr.hseon = val.hseon;
> > + /* Set external clock bypass state */
> > + s->rcc_cr.hsebyp = s->rcc_cr.hserdy && val.hsebyp;
> > + /* Set PLL state */
> > + s->rcc_cr.pllrdy = s->rcc_cr.pllon = val.pllon;
> > + /* Set I2S PLL state */
> > + s->rcc_cr.plli2srdy = s->rcc_cr.plli2son = val.plli2son;
> > +
> > + /* Update clock */
> > + rcc_update_clock(s);
> > +}
> > +
> > +static void rcc_pllcfgr_write(STM32F4xxRccState *s, RccPllcfgrType
> > +val) {
> > + /* Set PLL entry clock source */
> > + s->rcc_pllcfgr.pllsrc = val.pllsrc;
> > + /* Set main PLL input division factor */
> > + s->rcc_pllcfgr.pllm = val.pllm;
> > + /* Set main PLL multiplication factor for VCO */
> > + s->rcc_pllcfgr.plln = val.plln;
> > + /* Set main PLL output division factor */
> > + s->rcc_pllcfgr.pllp = val.pllp;
> > +
> > + /* Update clock */
> > + rcc_update_clock(s);
> > +}
> > +
> > +static void rcc_cfgr_write(STM32F4xxRccState *s, RccCfgrType val) {
> > + /* Set clock switch status */
> > + s->rcc_cfgr.sw = s->rcc_cfgr.sws = val.sw;
> > + /* Set AHB prescaler clock division factor */
> > + s->rcc_cfgr.hpre = val.hpre;
> > +
> > + /* Update clock */
> > + rcc_update_clock(s);
> > +}
> > +
> > +static void rcc_csr_write(STM32F4xxRccState *s, RccCsrType val) {
> > + /* Set internal low-speed oscillator state */
> > + s->rcc_csr.lsirdy = s->rcc_csr.lsion = val.lsion;
> > +
> > + /* Update clock */
> > + rcc_update_clock(s);
> > +}
> > +
> > +static uint64_t stm32f4xx_rcc_read(void *opaque, hwaddr addr,
> > + unsigned int size) {
> > + STM32F4xxRccState *s = opaque;
> > +
> > + trace_stm32f4xx_rcc_read(addr);
> > +
> > + switch (addr) {
> > + case RCC_CR:
> > + return s->rcc_cr.reg;
> > + case RCC_PLLCFGR:
> > + return s->rcc_pllcfgr.reg;
> > + case RCC_CFGR:
> > + return s->rcc_cfgr.reg;
> > + case RCC_CIR:
> > + qemu_log_mask(
> > + LOG_UNIMP,
> > + "%s: Clock interrupt configuration is not supported in QEMU\n",
> > + __func__);
> > + return s->rcc_cir.reg;
> > + case RCC_AHB1RSTR:
> > + return s->rcc_ahb1rstr.reg;
> > + case RCC_AHB2RSTR:
> > + return s->rcc_ahb2rstr.reg;
> > + case RCC_AHB3RSTR:
> > + return s->rcc_ahb3rstr.reg;
> > + case RCC_APB1RSTR:
> > + return s->rcc_apb1rstr.reg;
> > + case RCC_APB2RSTR:
> > + return s->rcc_apb2rstr.reg;
> > + case RCC_AHB1ENR:
> > + return s->rcc_ahb1enr.reg;
> > + case RCC_AHB2ENR:
> > + return s->rcc_ahb2enr.reg;
> > + case RCC_AHB3ENR:
> > + return s->rcc_ahb3enr.reg;
> > + case RCC_APB1ENR:
> > + return s->rcc_apb1enr.reg;
> > + case RCC_APB2ENR:
> > + return s->rcc_apb2enr.reg;
> > + case RCC_AHB1LPENR:
> > + return s->rcc_ahb1lpenr.reg;
> > + case RCC_AHB2LPENR:
> > + return s->rcc_ahb2lpenr.reg;
> > + case RCC_AHB3LPENR:
> > + return s->rcc_ahb3lpenr.reg;
> > + case RCC_APB1LPENR:
> > + return s->rcc_apb1lpenr.reg;
> > + case RCC_APB2LPENR:
> > + return s->rcc_apb2lpenr.reg;
> > + case RCC_BDCR:
> > + qemu_log_mask(
> > + LOG_UNIMP,
> > + "%s: Backup domain control is not supported in QEMU\n",
> > + __func__);
> > + return s->rcc_bdcr.reg;
> > + case RCC_CSR:
> > + return s->rcc_csr.reg;
> > + case RCC_SSCGR:
> > + qemu_log_mask(
> > + LOG_UNIMP,
> > + "%s: Spread spectrum clock generation is not supported in
> > QEMU\n",
> > + __func__);
> > + return s->rcc_sscgr.reg;
> > + case RCC_PLLI2SCFGR:
> > + qemu_log_mask(
> > + LOG_UNIMP,
> > + "%s: PLLI2S configuration is not supported in QEMU\n",
> > + __func__);
> > + return s->rcc_plli2scfgr.reg;
> > + default:
> > + qemu_log_mask(
> > + LOG_GUEST_ERROR,
> > + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
> > + return 0;
> > + }
> > +}
> > +
> > +static void stm32f4xx_rcc_write(void *opaque, hwaddr addr,
> > + uint64_t val64, unsigned int size) {
> > + STM32F4xxRccState *s = opaque;
> > + uint32_t value = val64;
> > +
> > + trace_stm32f4xx_rcc_write(value, addr);
> > +
> > + switch (addr) {
> > + case RCC_CR:
> > + rcc_cr_write(s, (RccCrType)value);
> > + return;
> > + case RCC_PLLCFGR:
> > + rcc_pllcfgr_write(s, (RccPllcfgrType)value);
> > + return;
> > + case RCC_CFGR:
> > + rcc_cfgr_write(s, (RccCfgrType)value);
> > + return;
> > + case RCC_CIR:
> > + qemu_log_mask(
> > + LOG_UNIMP,
> > + "%s: Clock interrupt configuration is not supported in QEMU\n",
> > + __func__);
> > + return;
> > + case RCC_AHB1RSTR ... RCC_APB2RSTR:
> > + qemu_log_mask(
> > + LOG_UNIMP,
> > + "%s: Peripheral reset is a no-op in QEMU\n", __func__);
> > + return;
> > + case RCC_AHB1ENR:
> > + /* Store peripheral enable status; otherwise, this is no-op */
> > + s->rcc_ahb1enr.reg = value;
> > + return;
> > + case RCC_AHB2ENR:
> > + s->rcc_ahb2enr.reg = value;
> > + return;
> > + case RCC_AHB3ENR:
> > + s->rcc_ahb3enr.reg = value;
> > + return;
> > + case RCC_APB1ENR:
> > + s->rcc_apb1enr.reg = value;
> > + return;
> > + case RCC_APB2ENR:
> > + s->rcc_apb2enr.reg = value;
> > + return;
> > + case RCC_AHB1LPENR:
> > + /* Store peripheral low power status; otherwise, this is no-op */
> > + s->rcc_ahb1lpenr.reg = value;
> > + return;
> > + case RCC_AHB2LPENR:
> > + s->rcc_ahb2lpenr.reg = value;
> > + return;
> > + case RCC_AHB3LPENR:
> > + s->rcc_ahb3lpenr.reg = value;
> > + return;
> > + case RCC_APB1LPENR:
> > + s->rcc_apb1lpenr.reg = value;
> > + return;
> > + case RCC_APB2LPENR:
> > + s->rcc_apb2lpenr.reg = value;
> > + return;
> > + case RCC_BDCR:
> > + qemu_log_mask(
> > + LOG_UNIMP,
> > + "%s: Backup domain control is not supported in QEMU\n",
> > + __func__);
> > + return;
> > + case RCC_CSR:
> > + rcc_csr_write(s, (RccCsrType)value);
> > + return;
> > + case RCC_SSCGR:
> > + qemu_log_mask(
> > + LOG_UNIMP,
> > + "%s: Spread spectrum clock generation is not supported in
> > QEMU\n",
> > + __func__);
> > + return;
> > + case RCC_PLLI2SCFGR:
> > + qemu_log_mask(
> > + LOG_UNIMP,
> > + "%s: PLLI2S configuration is not supported in QEMU\n",
> > + __func__);
> > + return;
> > + default:
> > + qemu_log_mask(
> > + LOG_GUEST_ERROR,
> > + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
> > + }
> > +}
> > +
> > +static const MemoryRegionOps stm32f4xx_rcc_ops = {
> > + .read = stm32f4xx_rcc_read,
> > + .write = stm32f4xx_rcc_write,
> > + .endianness = DEVICE_NATIVE_ENDIAN, };
> > +
> > +static void stm32f4xx_rcc_init(Object *obj) {
> > + STM32F4xxRccState *s = STM32F4XX_RCC(obj);
> > +
> > + memory_region_init_io(&s->mmio, obj, &stm32f4xx_rcc_ops, s,
> > + TYPE_STM32F4XX_RCC, 0x400);
> > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); }
> > +
> > +static const VMStateDescription vmstate_stm32f4xx_rcc = {
> > + .name = TYPE_STM32F4XX_RCC,
> > + .version_id = 1,
> > + .minimum_version_id = 1,
> > + .fields = (VMStateField[]) {
> > + VMSTATE_UINT32(rcc_cr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_pllcfgr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_cfgr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_cir.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_ahb1rstr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_ahb2rstr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_ahb3rstr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_apb1rstr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_apb2rstr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_ahb1enr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_ahb2enr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_ahb3enr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_apb1enr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_apb2enr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_ahb1lpenr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_ahb2lpenr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_ahb3lpenr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_apb1lpenr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_apb2lpenr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_bdcr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_csr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_sscgr.reg, STM32F4xxRccState),
> > + VMSTATE_UINT32(rcc_plli2scfgr.reg, STM32F4xxRccState),
> > + VMSTATE_END_OF_LIST()
> > + }
> > +};
> > +
> > +static Property stm32f4xx_rcc_properties[] = {
> > + DEFINE_PROP_UINT32("hse-frequency", STM32F4xxRccState, hse_frequency,
> > 0),
> > + DEFINE_PROP_END_OF_LIST()
> > +};
> > +
> > +static void stm32f4xx_rcc_class_init(ObjectClass *klass, void *data)
> > +{
> > + DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > + dc->reset = stm32f4xx_rcc_reset;
> > + dc->vmsd = &vmstate_stm32f4xx_rcc;
> > + device_class_set_props(dc, stm32f4xx_rcc_properties); }
> > +
> > +static const TypeInfo stm32f4xx_rcc_info = {
> > + .name = TYPE_STM32F4XX_RCC,
> > + .parent = TYPE_SYS_BUS_DEVICE,
> > + .instance_size = sizeof(STM32F4xxRccState),
> > + .instance_init = stm32f4xx_rcc_init,
> > + .class_init = stm32f4xx_rcc_class_init,
> > +};
> > +
> > +static void stm32f4xx_rcc_register_types(void)
> > +{
> > + type_register_static(&stm32f4xx_rcc_info);
> > +}
> > +
> > +type_init(stm32f4xx_rcc_register_types)
> > diff --git a/hw/misc/trace-events b/hw/misc/trace-events index
> > 7f0f5dff3a..0fa47598b5 100644
> > --- a/hw/misc/trace-events
> > +++ b/hw/misc/trace-events
> > @@ -84,6 +84,10 @@ mos6522_set_sr_int(void) "set sr_int"
> > mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 "
> > val=0x%"PRIx64 mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 "
> > val=0x%x"
> >
> > +# stm32f4xx_rcc
> > +stm32f4xx_rcc_read(uint64_t addr) "reg read: addr: 0x%" PRIx64 " "
> > +stm32f4xx_rcc_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%"
> > PRIx64 " val: 0x%" PRIx64 ""
> > +
> > # stm32f4xx_syscfg
> > stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interupt: GPIO:
> > %d, Line: %d; Level: %d"
> > stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d"
> > diff --git a/include/hw/arm/stm32f405_soc.h
> > b/include/hw/arm/stm32f405_soc.h index 1fe97f8c3a..037db62a58 100644
> > --- a/include/hw/arm/stm32f405_soc.h
> > +++ b/include/hw/arm/stm32f405_soc.h
> > @@ -25,6 +25,7 @@
> > #ifndef HW_ARM_STM32F405_SOC_H
> > #define HW_ARM_STM32F405_SOC_H
> >
> > +#include "hw/misc/stm32f4xx_rcc.h"
> > #include "hw/misc/stm32f4xx_syscfg.h"
> > #include "hw/timer/stm32f2xx_timer.h"
> > #include "hw/char/stm32f2xx_usart.h"
> > @@ -54,9 +55,11 @@ typedef struct STM32F405State {
> > /*< public >*/
> >
> > char *cpu_type;
> > + uint32_t hse_frequency;
> >
> > ARMv7MState armv7m;
> >
> > + STM32F4xxRccState rcc;
> > STM32F4xxSyscfgState syscfg;
> > STM32F4xxExtiState exti;
> > STM32F2XXUsartState usart[STM_NUM_USARTS]; diff --git
> > a/include/hw/misc/stm32f4xx_rcc.h b/include/hw/misc/stm32f4xx_rcc.h
> > new file mode 100644 index 0000000000..5c269753c0
> > --- /dev/null
> > +++ b/include/hw/misc/stm32f4xx_rcc.h
> > @@ -0,0 +1,316 @@
> > +/*
> > + * STM32F4xx RCC
> > + *
> > + * Copyright (c) 2020 Stephanos Ioannidis <address@hidden>
> > + *
> > + * Permission is hereby granted, free of charge, to any person
> > +obtaining a copy
> > + * of this software and associated documentation files (the
> > +"Software"), to deal
> > + * in the Software without restriction, including without limitation
> > +the rights
> > + * to use, copy, modify, merge, publish, distribute, sublicense,
> > +and/or sell
> > + * copies of the Software, and to permit persons to whom the Software
> > +is
> > + * furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice shall be
> > +included in
> > + * all copies or substantial portions of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> > +EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > +MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
> > +SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
> > +OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> > +ARISING FROM,
> > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> > +DEALINGS IN
> > + * THE SOFTWARE.
> > + */
> > +
> > +#ifndef HW_STM_RCC_H
> > +#define HW_STM_RCC_H
> > +
> > +#include "hw/sysbus.h"
> > +#include "hw/hw.h"
> > +
> > +#define TYPE_STM32F4XX_RCC "stm32f4xx-rcc"
> > +#define STM32F4XX_RCC(obj) \
> > + OBJECT_CHECK(STM32F4xxRccState, (obj), TYPE_STM32F4XX_RCC)
> > +
> > +#define RCC_CR 0x00
> > +#define RCC_PLLCFGR 0x04
> > +#define RCC_CFGR 0x08
> > +#define RCC_CIR 0x0C
> > +#define RCC_AHB1RSTR 0x10
> > +#define RCC_AHB2RSTR 0x14
> > +#define RCC_AHB3RSTR 0x18
> > +#define RCC_APB1RSTR 0x20
> > +#define RCC_APB2RSTR 0x24
> > +#define RCC_AHB1ENR 0x30
> > +#define RCC_AHB2ENR 0x34
> > +#define RCC_AHB3ENR 0x38
> > +#define RCC_APB1ENR 0x40
> > +#define RCC_APB2ENR 0x44
> > +#define RCC_AHB1LPENR 0x50
> > +#define RCC_AHB2LPENR 0x54
> > +#define RCC_AHB3LPENR 0x58
> > +#define RCC_APB1LPENR 0x60
> > +#define RCC_APB2LPENR 0x64
> > +#define RCC_BDCR 0x70
> > +#define RCC_CSR 0x74
> > +#define RCC_SSCGR 0x80
> > +#define RCC_PLLI2SCFGR 0x84
> > +
> > +typedef union {
> > + struct {
> > + uint32_t hsion : 1;
> > + uint32_t hsirdy : 1;
> > + uint32_t reserved0 : 1;
> > + uint32_t hsitrim : 5;
> > + uint32_t hsical : 8;
> > + uint32_t hseon : 1;
> > + uint32_t hserdy : 1;
> > + uint32_t hsebyp : 1;
> > + uint32_t csson : 1;
> > + uint32_t reserved1 : 4;
> > + uint32_t pllon : 1;
> > + uint32_t pllrdy : 1;
> > + uint32_t plli2son : 1;
> > + uint32_t plli2srdy : 1;
> > + uint32_t reserved2 : 4;
> > + };
> > + uint32_t reg;
> > +} RccCrType;
> > +
> > +typedef union {
> > + struct {
> > + uint32_t pllm : 6;
> > + uint32_t plln : 9;
> > + uint32_t reserved0 : 1;
> > + uint32_t pllp : 2;
> > + uint32_t reserved1 : 4;
> > + uint32_t pllsrc : 1;
> > + uint32_t reserved2 : 1;
> > + uint32_t pllq : 4;
> re?> + uint32_t reserved3 : 4;
> > + };
> > + uint32_t reg;
> > +} RccPllcfgrType;
> > +
> > +typedef union {
> > + struct {
> > + uint32_t sw : 2;
> > + uint32_t sws : 2;
> > + uint32_t hpre : 4;
> > + uint32_t reserved0 : 2;
> > + uint32_t ppre1 : 3;
> > + uint32_t ppre2: 3;
> > + uint32_t rtcpre : 5;
> > + uint32_t mco1 : 2;
> > + uint32_t i2sscr : 1;
> > + uint32_t mco1pre : 3;
> > + uint32_t mco2pre : 3;
> > + uint32_t mco2 : 2;
> > + };
> > + uint32_t reg;
> > +} RccCfgrType;
> > +
> > +typedef union {
> > + struct {
> > + uint32_t lsirdyf : 1;
> > + uint32_t lserdyf : 1;
> > + uint32_t hsirdyf : 1;
> > + uint32_t hserdyf : 1;
> > + uint32_t pllrdyf : 1;
> > + uint32_t plli2srdyf : 1;
> > + uint32_t reserved0 : 1;
>
> Shouldn't this be PLLSAIRDYF?
>
> > + uint32_t cssf : 1;
> > + uint32_t lsirdyie : 1;
> > + uint32_t lserdyie : 1;
> > + uint32_t hsirdyie : 1;
> > + uint32_t hserdyie : 1;
> > + uint32_t pllrdyie : 1;
> > + uint32_t plli2srdyie : 1;
>
> Aren't you missing a PLLSAIRDYIE
>
> Alistair
>
> > + uint32_t reserved1 : 2;
> > + uint32_t lsirdyc : 1;
> > + uint32_t lserdyc : 1;
> > + uint32_t hsirdyc : 1;
> > + uint32_t hserdyc : 1;
> > + uint32_t pllrdyc : 1;
> > + uint32_t plli2srdyc : 1;
> > + uint32_t reserved2 : 1;
> > + uint32_t cssc : 1;
> > + uint32_t reserved3 : 8;
> > + };
> > + uint32_t reg;
> > +} RccCirType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccAhb1rstrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccAhb2rstrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccAhb3rstrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccApb1rstrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccApb2rstrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccAhb1enrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccAhb2enrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccAhb3enrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccApb1enrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccApb2enrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccAhb1lpenrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccAhb2lpenrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccAhb3lpenrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccApb1lpenrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccApb2lpenrType;
> > +
> > +typedef union {
> > + struct {
> > + uint32_t lseon : 1;
> > + uint32_t lserdy : 1;
> > + uint32_t lsebyp : 1;
> > + uint32_t reserved0 : 5;
> > + uint32_t rtcsel : 2;
> > + uint32_t reserved1 : 5;
> > + uint32_t rtcen : 1;
> > + uint32_t bdrst : 1;
> > + uint32_t reserved2 : 15;
> > + };
> > + uint32_t reg;
> > +} RccBdcrType;
> > +
> > +typedef union {
> > + struct {
> > + uint32_t lsion : 1;
> > + uint32_t lsirdy : 1;
> > + uint32_t reserved0 : 22;
> > + uint32_t rmvf : 1;
> > + uint32_t borrstf : 1;
> > + uint32_t pinrstf : 1;
> > + uint32_t porrstf : 1;
> > + uint32_t sftrstf : 1;
> > + uint32_t iwdgrstf : 1;
> > + uint32_t wwdgrstf : 1;
> > + uint32_t lpwrrstf : 1;
> > + };
> > + uint32_t reg;
> > +} RccCsrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccSscgrType;
> > +
> > +typedef struct {
> > + /* Fields are not specified */
> > + uint32_t reg;
> > +} RccPlli2scfgrType;
> > +
> > +typedef struct {
> > + /* <private> */
> > + SysBusDevice parent_obj;
> > +
> > + /* <public> */
> > + MemoryRegion mmio;
> > + uint32_t hse_frequency;
> > +
> > + /* Clock control register (RCC_CR) */
> > + RccCrType rcc_cr;
> > + /* PLL configuration register (RCC_PLLCFGR) */
> > + RccPllcfgrType rcc_pllcfgr;
> > + /* Clock configuration register (RCC_CFGR) */
> > + RccCfgrType rcc_cfgr;
> > + /* Clock interrupt register (RCC_CIR) */
> > + RccCirType rcc_cir;
> > + /* AHB1 peripheral reset register (RCC_AHB1RSTR) */
> > + RccAhb1rstrType rcc_ahb1rstr;
> > + /* AHB2 peripheral reset register (RCC_AHB2RSTR) */
> > + RccAhb2rstrType rcc_ahb2rstr;
> > + /* AHB3 peripheral reset register (RCC_AHB3RSTR) */
> > + RccAhb3rstrType rcc_ahb3rstr;
> > + /* APB1 peripheral reset register (RCC_APB1RSTR) */
> > + RccApb1rstrType rcc_apb1rstr;
> > + /* APB2 peripheral reset register (RCC_APB2RSTR) */
> > + RccApb2rstrType rcc_apb2rstr;
> > + /* AHB1 peripheral clock register (RCC_AHB1ENR) */
> > + RccAhb1enrType rcc_ahb1enr;
> > + /* AHB2 peripheral clock register (RCC_AHB2ENR) */
> > + RccAhb2enrType rcc_ahb2enr;
> > + /* AHB3 peripheral clock register (RCC_AHB3ENR) */
> > + RccAhb3enrType rcc_ahb3enr;
> > + /* APB1 peripheral clock register (RCC_APB1ENR) */
> > + RccApb1enrType rcc_apb1enr;
> > + /* APB2 peripheral clock register (RCC_APB1ENR) */
> > + RccApb2enrType rcc_apb2enr;
> > + /* AHB1 peripheral low power mode register (RCC_AHB1LPENR) */
> > + RccAhb1lpenrType rcc_ahb1lpenr;
> > + /* AHB2 peripheral low power mode register (RCC_AHB2LPENR) */
> > + RccAhb2lpenrType rcc_ahb2lpenr;
> > + /* AHB3 peripheral low power mode register (RCC_AHB3LPENR) */
> > + RccAhb3lpenrType rcc_ahb3lpenr;
> > + /* APB1 peripheral low power mode register (RCC_APB1LPENR) */
> > + RccApb1lpenrType rcc_apb1lpenr;
> > + /* APB2 peripheral low power mode register (RCC_APB2LPENR) */
> > + RccApb2lpenrType rcc_apb2lpenr;
> > + /* Backup domain control register (RCC_BDCR) */
> > + RccBdcrType rcc_bdcr;
> > + /* Clock control and status register (RCC_CSR) */
> > + RccCsrType rcc_csr;
> > + /* Spread spectrum clock generation register (RCC_SSCGR) */
> > + RccSscgrType rcc_sscgr;
> > + /* PLLI2S configuration register (RCC_PLLI2SCFGR) */
> > + RccPlli2scfgrType rcc_plli2scfgr; } STM32F4xxRccState;
> > +
> > +#endif
> > --
> > 2.17.1
> >
> >