[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
RE: [PATCH] hw/arm/stm32f405: Add preliminary RCC emulation support
From: |
Stephanos Ioannidis |
Subject: |
RE: [PATCH] hw/arm/stm32f405: Add preliminary RCC emulation support |
Date: |
Fri, 6 Mar 2020 01:06:11 +0000 |
> 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).
> 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.
> 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.
> 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.
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
>
>