---
hw/misc/stm32l4x5_rcc.c | 164 ++++++++++++++++++++++++++++------------
1 file changed, 114 insertions(+), 50 deletions(-)
diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
index 1f55662bbd..325b179d10 100644
--- a/hw/misc/stm32l4x5_rcc.c
+++ b/hw/misc/stm32l4x5_rcc.c
@@ -329,9 +329,47 @@ static void rcc_update_irq(Stm32l4x5RccState *s)
}
}
-static void rcc_update_cr_register(Stm32l4x5RccState *s)
+static void rcc_update_msi(Stm32l4x5RccState *s, uint32_t previous_value)
+{
+ uint32_t val;
+
+ static const uint32_t msirange[] = {
+ 100000, 200000, 400000, 800000, 1000000, 2000000,
+ 4000000, 8000000, 16000000, 24000000, 32000000, 48000000
+ };
+ /* MSIRANGE and MSIRGSEL */
+ val = extract32(s->cr, R_CR_MSIRGSEL_SHIFT, R_CR_MSIRGSEL_LENGTH);
+ if (val) {
+ /* MSIRGSEL is set, use the MSIRANGE field */
+ val = extract32(s->cr, R_CR_MSIRANGE_SHIFT, R_CR_MSIRANGE_LENGTH);
+ } else {
+ /* MSIRGSEL is not set, use the MSISRANGE field */
+ val = extract32(s->csr, R_CSR_MSISRANGE_SHIFT, R_CSR_MSISRANGE_LENGTH);
+ }
+
+ if (val < ARRAY_SIZE(msirange)) {
+ clock_update_hz(s->msi_rc, msirange[val]);
+ } else {
+ /*
+ * There is a hardware write protection if the value is out of bound.
+ * Restore the previous value.
+ */
+ s->cr = (s->cr & ~R_CSR_MSISRANGE_MASK) |
+ (previous_value & R_CSR_MSISRANGE_MASK);
+ }
+}
+
+/*
+ * TODO: Add write-protection for all registers:
+ * DONE: CR
+ */
+
+static void rcc_update_cr_register(Stm32l4x5RccState *s, uint32_t previous_value)
{
int val;
+ const RccClockMuxSource current_pll_src =
+ CLOCK_MUX_INIT_INFO[RCC_CLOCK_MUX_PLL_INPUT].src_mapping[
+ s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT].src];
/* PLLSAI2ON and update PLLSAI2RDY */
val = extract32(s->cr, R_CR_PLLSAI2ON_SHIFT, R_CR_PLLSAI2ON_LENGTH);
@@ -351,77 +389,101 @@ static void rcc_update_cr_register(Stm32l4x5RccState *s)
s->cifr |= R_CIFR_PLLSAI1RDYF_MASK;
}
- /* PLLON and update PLLRDY */
+ /*
+ * PLLON and update PLLRDY
+ * PLLON cannot be reset if the PLL clock is used as the system clock.
+ */
val = extract32(s->cr, R_CR_PLLON_SHIFT, R_CR_PLLON_LENGTH);
- pll_set_enable(&s->plls[RCC_PLL_PLL], val);
- s->cr = (s->cr & ~R_CR_PLLRDY_MASK) |
- (val << R_CR_PLLRDY_SHIFT);
- if (s->cier & R_CIER_PLLRDYIE_MASK) {
- s->cifr |= R_CIFR_PLLRDYF_MASK;
+ if (extract32(s->cfgr, R_CFGR_SWS_SHIFT, R_CFGR_SWS_LENGTH) != 0b11) {
+ pll_set_enable(&s->plls[RCC_PLL_PLL], val);
+ s->cr = (s->cr & ~R_CR_PLLRDY_MASK) |
+ (val << R_CR_PLLRDY_SHIFT);
+ if (s->cier & R_CIER_PLLRDYIE_MASK) {
+ s->cifr |= R_CIFR_PLLRDYF_MASK;
+ }
+ } else {
+ s->cr |= R_CR_PLLON_MASK;
}
/* CSSON: TODO */
/* HSEBYP: TODO */
- /* HSEON and update HSERDY */
+ /*
+ * HSEON and update HSERDY.
+ * HSEON cannot be reset if the HSE oscillator is used directly or
+ * indirectly as the system clock.
+ */
val = extract32(s->cr, R_CR_HSEON_SHIFT, R_CR_HSEON_LENGTH);
- s->cr = (s->cr & ~R_CR_HSERDY_MASK) |
- (val << R_CR_HSERDY_SHIFT);
- if (val) {
- clock_update_hz(s->hse, s->hse_frequency);
- if (s->cier & R_CIER_HSERDYIE_MASK) {
- s->cifr |= R_CIFR_HSERDYF_MASK;
+ if (extract32(s->cfgr, R_CFGR_SWS_SHIFT, R_CFGR_SWS_LENGTH) != 0b10 &&
+ current_pll_src != RCC_CLOCK_MUX_SRC_HSE) {
+ s->cr = (s->cr & ~R_CR_HSERDY_MASK) |
+ (val << R_CR_HSERDY_SHIFT);
+ if (val) {
+ clock_update_hz(s->hse, s->hse_frequency);
+ if (s->cier & R_CIER_HSERDYIE_MASK) {
+ s->cifr |= R_CIFR_HSERDYF_MASK;
+ }
+ } else {
+ clock_update_hz(s->hse, 0);
}
} else {
- clock_update_hz(s->hse, 0);
+ s->cr |= R_CR_HSEON_MASK;
}
/* HSIAFS: TODO*/
/* HSIKERON: TODO*/
- /* HSION and update HSIRDY*/
- val = extract32(s->cr, R_CR_HSION_SHIFT, R_CR_HSION_LENGTH);
- s->cr = (s->cr & ~R_CR_HSIRDY_MASK) |
- (val << R_CR_HSIRDY_SHIFT);
- if (val) {
+ /*
+ * HSION and update HSIRDY
+ * HSION is set by hardware if the HSI16 is used directly
+ * or indirectly as system clock.
+ */
+ if (extract32(s->cfgr, R_CFGR_SWS_SHIFT, R_CFGR_SWS_LENGTH) == 0b01 ||
+ current_pll_src == RCC_CLOCK_MUX_SRC_HSI) {
+ s->cr |= (R_CR_HSION_MASK | R_CR_HSIRDY_MASK);
clock_update_hz(s->hsi16_rc, HSI_FRQ);
if (s->cier & R_CIER_HSIRDYIE_MASK) {
s->cifr |= R_CIFR_HSIRDYF_MASK;
}
} else {
- clock_update_hz(s->hsi16_rc, 0);
+ val = extract32(s->cr, R_CR_HSION_SHIFT, R_CR_HSION_LENGTH);
+ if (val) {
+ clock_update_hz(s->hsi16_rc, HSI_FRQ);
+ s->cr |= R_CR_HSIRDY_MASK;
+ if (s->cier & R_CIER_HSIRDYIE_MASK) {
+ s->cifr |= R_CIFR_HSIRDYF_MASK;
+ }
+ } else {
+ clock_update_hz(s->hsi16_rc, 0);
+ s->cr &= ~R_CR_HSIRDY_MASK;
+ }
}
- static const uint32_t msirange[] = {
- 100000, 200000, 400000, 800000, 1000000, 2000000,
- 4000000, 8000000, 16000000, 24000000, 32000000, 48000000
- };
- /* MSIRANGE and MSIRGSEL */
- val = extract32(s->cr, R_CR_MSIRGSEL_SHIFT, R_CR_MSIRGSEL_LENGTH);
- if (val) {
- /* MSIRGSEL is set, use the MSIRANGE field */
- val = extract32(s->cr, R_CR_MSIRANGE_SHIFT, R_CR_MSIRANGE_LENGTH);
- } else {
- /* MSIRGSEL is not set, use the MSISRANGE field */
- val = extract32(s->csr, R_CSR_MSISRANGE_SHIFT, R_CSR_MSISRANGE_LENGTH);
- }
+ /* MSIPLLEN: TODO */
- if (val < ARRAY_SIZE(msirange)) {
- clock_update_hz(s->msi_rc, msirange[val]);
+ /*
+ * MSION and update MSIRDY
+ * Set by hardware when used directly or indirectly as system clock.
+ */
+ if (extract32(s->cfgr, R_CFGR_SWS_SHIFT, R_CFGR_SWS_LENGTH) == 0b00 ||
+ current_pll_src == RCC_CLOCK_MUX_SRC_MSI) {
+ s->cr |= (R_CR_MSION_MASK | R_CR_MSIRDY_MASK);
+ if (!(previous_value & R_CR_MSION_MASK) && (s->cier & R_CIER_MSIRDYIE_MASK)) {
+ s->cifr |= R_CIFR_MSIRDYF_MASK;
+ }
+ rcc_update_msi(s, previous_value);
} else {
- clock_update_hz(s->msi_rc, MSI_DEFAULT_FRQ);
- /* TODO: there is a write protection if the value is out of bound,
- implement that instead of setting the default */
- }
-
- /* MSIPLLEN */
-
- /* MSION and update MSIRDY */
- val = extract32(s->cr, R_CR_MSION_SHIFT, R_CR_MSION_LENGTH);
- s->cr = (s->cr & ~R_CR_MSIRDY_MASK) |
- (val << R_CR_MSIRDY_SHIFT);
- if (s->cier & R_CIER_MSIRDYIE_MASK) {
- s->cifr |= R_CIFR_MSIRDYF_MASK;
+ val = extract32(s->cr, R_CR_MSION_SHIFT, R_CR_MSION_LENGTH);
+ if (val) {
+ s->cr |= R_CR_MSIRDY_MASK;
+ rcc_update_msi(s, previous_value);
+ if (s->cier & R_CIER_MSIRDYIE_MASK) {
+ s->cifr |= R_CIFR_MSIRDYF_MASK;
+ }
+ } else {
+ s->cr &= ~R_CR_MSIRDY_MASK;
+ clock_update_hz(s->msi_rc, 0);
+ }
}
rcc_update_irq(s);
}
@@ -946,15 +1008,17 @@ static void stm32l4x5_rcc_write(void *opaque, hwaddr addr,
uint64_t val64, unsigned int size)
{
Stm32l4x5RccState *s = opaque;
+ uint32_t previous_value = 0;
const uint32_t value = val64;
trace_stm32l4x5_rcc_write(addr, value);
switch (addr) {
case A_CR:
+ previous_value = s->cr;
s->cr = (s->cr & CR_READ_SET_MASK) |
(value & (CR_READ_SET_MASK | ~CR_READ_ONLY_MASK));
- rcc_update_cr_register(s);
+ rcc_update_cr_register(s, previous_value);
break;
case A_ICSCR:
s->icscr = value & ~ICSCR_READ_ONLY_MASK;
--
2.34.1
Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
---
hw/arm/b-l475e-iot01a.c | 10 +---------
hw/arm/stm32l4x5_soc.c | 33 ++++-----------------------------
include/hw/arm/stm32l4x5_soc.h | 3 ---
3 files changed, 5 insertions(+), 41 deletions(-)
diff --git a/hw/arm/b-l475e-iot01a.c b/hw/arm/b-l475e-iot01a.c
index 6ecde2db15..d862aa43fc 100644
--- a/hw/arm/b-l475e-iot01a.c
+++ b/hw/arm/b-l475e-iot01a.c
@@ -26,27 +26,19 @@
#include "qapi/error.h"
#include "hw/boards.h"
#include "hw/qdev-properties.h"
-#include "hw/qdev-clock.h"
#include "qemu/error-report.h"
#include "hw/arm/stm32l4x5_soc.h"
#include "hw/arm/boot.h"
-/* Main SYSCLK frequency in Hz (80MHz) */
-#define MAIN_SYSCLK_FREQ_HZ 80000000ULL
+/* B-L475E-IOT01A implementation is derived from netduinoplus2 */
static void b_l475e_iot01a_init(MachineState *machine)
{
const Stm32l4x5SocClass *sc;
DeviceState *dev;
- Clock *sysclk;
-
- /* This clock doesn't need migration because it is fixed-frequency */
- sysclk = clock_new(OBJECT(machine), "SYSCLK");
- clock_set_hz(sysclk, MAIN_SYSCLK_FREQ_HZ);
dev = qdev_new(TYPE_STM32L4X5XG_SOC);
object_property_add_child(OBJECT(machine), "soc", OBJECT(dev));
- qdev_connect_clock_in(dev, "sysclk", sysclk);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sc = STM32L4X5_SOC_GET_CLASS(dev);
diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c
index 07c841027a..bcdad69e92 100644
--- a/hw/arm/stm32l4x5_soc.c
+++ b/hw/arm/stm32l4x5_soc.c
@@ -85,9 +85,6 @@ static void stm32l4x5_soc_initfn(Object *obj)
object_initialize_child(obj, "exti", &s->exti, TYPE_STM32L4X5_EXTI);
object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32L4X5_SYSCFG);
object_initialize_child(obj, "rcc", &s->rcc, TYPE_STM32L4X5_RCC);
-
- 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 stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
@@ -99,30 +96,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
DeviceState *armv7m;
SysBusDevice *busdev;
- /*
- * 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);
-
if (!memory_region_init_rom(&s->flash, OBJECT(dev_soc), "flash",
sc->flash_size, errp)) {
return;
@@ -151,8 +124,10 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
qdev_prop_set_uint32(armv7m, "num-irq", 96);
qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4"));
qdev_prop_set_bit(armv7m, "enable-bitband", true);
- qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
- qdev_connect_clock_in(armv7m, "refclk", s->refclk);
+ qdev_connect_clock_in(armv7m, "cpuclk",
+ qdev_get_clock_out(DEVICE(&(s->rcc)), "cortex-fclk-out"));
+ qdev_connect_clock_in(armv7m, "refclk",
+ qdev_get_clock_out(DEVICE(&(s->rcc)), "cortex-refclk-out"));
object_property_set_link(OBJECT(&s->armv7m), "memory",
OBJECT(system_memory), &error_abort);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h
index e480fcc976..1f71298b45 100644
--- a/include/hw/arm/stm32l4x5_soc.h
+++ b/include/hw/arm/stm32l4x5_soc.h
@@ -50,9 +50,6 @@ struct Stm32l4x5SocState {
MemoryRegion sram2;
MemoryRegion flash;
MemoryRegion flash_alias;
-
- Clock *sysclk;
- Clock *refclk;
};
struct Stm32l4x5SocClass {
--
2.34.1
Tests:
- the ability to change the sysclk of the device
- the ability to enable/disable/configure the PLLs
- if the clock multiplexers work
- the register flags and the generation of irqs
Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
---
tests/qtest/meson.build | 3 +-
tests/qtest/stm32l4x5_rcc-test.c | 207 +++++++++++++++++++++++++++++++
2 files changed, 209 insertions(+), 1 deletion(-)
create mode 100644 tests/qtest/stm32l4x5_rcc-test.c
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index a926af92f6..b0d9a8c2de 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -197,7 +197,8 @@ qtests_aspeed = \
qtests_stm32l4x5 = \
['stm32l4x5_exti-test',
- 'stm32l4x5_syscfg-test']
+ 'stm32l4x5_syscfg-test',
+ 'stm32l4x5_rcc-test']
qtests_arm = \
(config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \
diff --git a/tests/qtest/stm32l4x5_rcc-test.c b/tests/qtest/stm32l4x5_rcc-test.c
new file mode 100644
index 0000000000..4157291052
--- /dev/null
+++ b/tests/qtest/stm32l4x5_rcc-test.c
@@ -0,0 +1,207 @@
+/*
+ * QTest testcase for STM32L4x5_RCC
+ *
+ * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
+ * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/registerfields.h"
+#include "libqtest-single.h"
+#include "hw/misc/stm32l4x5_rcc_internals.h"
+
+#define RCC_BASE_ADDR 0x40021000
+#define NVIC_ISER 0xE000E100
+#define NVIC_ISPR 0xE000E200
+#define NVIC_ICPR 0xE000E280
+#define RCC_IRQ 5
+
+static void enable_nvic_irq(unsigned int n)
+{
+ writel(NVIC_ISER, 1 << n);
+}
+
+static void unpend_nvic_irq(unsigned int n)
+{
+ writel(NVIC_ICPR, 1 << n);
+}
+
+static bool check_nvic_pending(unsigned int n)
+{
+ return readl(NVIC_ISPR) & (1 << n);
+}
+
+static bool qts_wait_for_flag(QTestState *qts, uint32_t event_addr,
+ uint32_t flag, uint32_t value)
+{
+ /* Wait at most 5 seconds */
+ for (int i = 0; i < 5000; i++) {
+ if ((qtest_readl(qts, event_addr) & flag) == value) {
+ return true;
+ }
+ g_usleep(1000);
+ }
+
+ return false;
+}
+
+static bool rcc_wait_for_flag(uint32_t event_addr, uint32_t flag,
+ uint32_t value)
+{
+ return qts_wait_for_flag(global_qtest, RCC_BASE_ADDR + event_addr, flag, value);
+}
+
+static void rcc_writel(unsigned int offset, uint32_t value)
+{
+ writel(RCC_BASE_ADDR + offset, value);
+}
+
+static uint32_t rcc_readl(unsigned int offset)
+{
+ return readl(RCC_BASE_ADDR + offset);
+}
+
+static void test_init_msi(void)
+{
+ /* MSIRANGE can be set only when MSI is OFF or READY */
+ rcc_writel(A_CR, R_CR_MSION_MASK);
+ /* Wait until MSI is stable */
+ g_assert_true(rcc_wait_for_flag(A_CR, R_CR_MSIRDY_MASK, R_CR_MSIRDY_MASK));
+ /* TODO find a way to test MSI value */
+}
+
+static void test_set_msi_as_sysclk(void)
+{
+ /* Clocking from MSI, in case MSI was not the default source */
+ rcc_writel(A_CFGR, 0);
+ /* Wait until MSI is selected and stable */
+ g_assert_true(rcc_wait_for_flag(A_CFGR, R_CFGR_SWS_MASK, 0));
+}
+
+static void test_init_pll(void)
+{
+ uint32_t value;
+
+ /*
+ * Update PLL and set MSI as the source clock.
+ * PLLM = 1 --> 000
+ * PLLN = 40 --> 40
+ * PPLLR = 2 --> 00
+ * PLLDIV = unused, PLLP = unused (SAI3), PLLQ = unused (48M1)
+ * SRC = MSI --> 01
+ */
+ rcc_writel(A_PLLCFGR, R_PLLCFGR_PLLREN_MASK |
+ (40 << R_PLLCFGR_PLLN_SHIFT) |
+ (0b01 << R_PLLCFGR_PLLSRC_SHIFT));
+
+ /* PLL activation */
+ value = rcc_readl(A_CR);
+ rcc_writel(A_CR, value | R_CR_PLLON_MASK);
+
+ /* Waiting for PLL lock. */
+ g_assert_true(rcc_wait_for_flag(A_CR, R_CR_PLLRDY_MASK, R_CR_PLLRDY_MASK));
+
+ /* Switches on the PLL clock source */
+ value = rcc_readl(A_CFGR);
+ rcc_writel(A_CFGR, (value & ~R_CFGR_SW_MASK) |
+ (0b11 << R_CFGR_SW_SHIFT));
+
+ /* Wait until SYSCLK is stable. */
+ g_assert_true(rcc_wait_for_flag(A_CFGR, R_CFGR_SWS_MASK,
+ (0b11 << R_CFGR_SWS_SHIFT)));
+}
+
+static void test_activate_lse(void)
+{
+ /* LSE activation, no LSE Bypass */
+ rcc_writel(A_BDCR, R_BDCR_LSEDRV_MASK | R_BDCR_LSEON_MASK);
+ g_assert_true(rcc_wait_for_flag(A_BDCR, R_BDCR_LSERDY_MASK, R_BDCR_LSERDY_MASK));
+}
+
+static void test_irq(void)
+{
+ enable_nvic_irq(RCC_IRQ);
+
+ rcc_writel(A_CIER, R_CIER_LSIRDYIE_MASK);
+ rcc_writel(A_CSR, R_CSR_LSION_MASK);
+ g_assert_true(check_nvic_pending(RCC_IRQ));
+ rcc_writel(A_CICR, R_CICR_LSIRDYC_MASK);
+ unpend_nvic_irq(RCC_IRQ);
+
+ rcc_writel(A_CIER, R_CIER_LSERDYIE_MASK);
+ rcc_writel(A_BDCR, R_BDCR_LSEON_MASK);
+ g_assert_true(check_nvic_pending(RCC_IRQ));
+ rcc_writel(A_CICR, R_CICR_LSERDYC_MASK);
+ unpend_nvic_irq(RCC_IRQ);
+
+ /*
+ * MSI has been enabled by previous tests,
+ * shouln't generate an interruption.
+ */
+ rcc_writel(A_CIER, R_CIER_MSIRDYIE_MASK);
+ rcc_writel(A_CR, R_CR_MSION_MASK);
+ g_assert_false(check_nvic_pending(RCC_IRQ));
+
+ rcc_writel(A_CIER, R_CIER_HSIRDYIE_MASK);
+ rcc_writel(A_CR, R_CR_HSION_MASK);
+ g_assert_true(check_nvic_pending(RCC_IRQ));
+ rcc_writel(A_CICR, R_CICR_HSIRDYC_MASK);
+ unpend_nvic_irq(RCC_IRQ);
+
+ rcc_writel(A_CIER, R_CIER_HSERDYIE_MASK);
+ rcc_writel(A_CR, R_CR_HSEON_MASK);
+ g_assert_true(check_nvic_pending(RCC_IRQ));
+ rcc_writel(A_CICR, R_CICR_HSERDYC_MASK);
+ unpend_nvic_irq(RCC_IRQ);
+
+ /*
+ * PLL has been enabled by previous tests,
+ * shouln't generate an interruption.
+ */
+ rcc_writel(A_CIER, R_CIER_PLLRDYIE_MASK);
+ rcc_writel(A_CR, R_CR_PLLON_MASK);
+ g_assert_false(check_nvic_pending(RCC_IRQ));
+
+ rcc_writel(A_CIER, R_CIER_PLLSAI1RDYIE_MASK);
+ rcc_writel(A_CR, R_CR_PLLSAI1ON_MASK);
+ g_assert_true(check_nvic_pending(RCC_IRQ));
+ rcc_writel(A_CICR, R_CICR_PLLSAI1RDYC_MASK);
+ unpend_nvic_irq(RCC_IRQ);
+
+ rcc_writel(A_CIER, R_CIER_PLLSAI2RDYIE_MASK);
+ rcc_writel(A_CR, R_CR_PLLSAI2ON_MASK);
+ g_assert_true(check_nvic_pending(RCC_IRQ));
+ rcc_writel(A_CICR, R_CICR_PLLSAI2RDYC_MASK);
+ unpend_nvic_irq(RCC_IRQ);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+ g_test_set_nonfatal_assertions();
+ /*
+ * These test separately that we can enable the plls, change the sysclk,
+ * and enable different devices.
+ * They are dependent on one another.
+ */
+ qtest_add_func("stm32l4x5/rcc/init_msi", test_init_msi);
+ qtest_add_func("stm32l4x5/rcc/set_msi_as_sysclk",
+ test_set_msi_as_sysclk);
+ qtest_add_func("stm32l4x5/rcc/activate_lse", test_activate_lse);
+ qtest_add_func("stm32l4x5/rcc/init_pll", test_init_pll);
+
+ qtest_add_func("stm32l4x5/rcc/irq", test_irq);
+
+ qtest_start("-machine b-l475e-iot01a");
+ ret = g_test_run();
+ qtest_end();
+
+ return ret;
+}
--
2.34.1
© 2016 - 2026 Red Hat, Inc.