hw/misc/stm32l4x5_rcc: Add write protections to CR register

Add write protections for the fields in the CR register.
PLL configuration write protections (among others) have not
been handled yet. This is planned in a future patch set.

Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
Message-id: 20240303140643.81957-7-arnaud.minier@telecom-paris.fr
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Arnaud Minier 2024-03-03 15:06:41 +01:00 committed by Peter Maydell
parent 9c796d503f
commit 3b55147717

View File

@ -374,9 +374,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; 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 */ /* PLLSAI2ON and update PLLSAI2RDY */
val = FIELD_EX32(s->cr, CR, PLLSAI2ON); val = FIELD_EX32(s->cr, CR, PLLSAI2ON);
@ -396,77 +434,101 @@ static void rcc_update_cr_register(Stm32l4x5RccState *s)
s->cifr |= R_CIFR_PLLSAI1RDYF_MASK; 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 = FIELD_EX32(s->cr, CR, PLLON); val = FIELD_EX32(s->cr, CR, PLLON);
pll_set_enable(&s->plls[RCC_PLL_PLL], val); if (FIELD_EX32(s->cfgr, CFGR, SWS) != 0b11) {
s->cr = (s->cr & ~R_CR_PLLRDY_MASK) | pll_set_enable(&s->plls[RCC_PLL_PLL], val);
(val << R_CR_PLLRDY_SHIFT); s->cr = (s->cr & ~R_CR_PLLRDY_MASK) |
if (s->cier & R_CIER_PLLRDYIE_MASK) { (val << R_CR_PLLRDY_SHIFT);
s->cifr |= R_CIFR_PLLRDYF_MASK; if (s->cier & R_CIER_PLLRDYIE_MASK) {
s->cifr |= R_CIFR_PLLRDYF_MASK;
}
} else {
s->cr |= R_CR_PLLON_MASK;
} }
/* CSSON: TODO */ /* CSSON: TODO */
/* HSEBYP: 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 = FIELD_EX32(s->cr, CR, HSEON); val = FIELD_EX32(s->cr, CR, HSEON);
s->cr = (s->cr & ~R_CR_HSERDY_MASK) | if (FIELD_EX32(s->cfgr, CFGR, SWS) != 0b10 &&
(val << R_CR_HSERDY_SHIFT); current_pll_src != RCC_CLOCK_MUX_SRC_HSE) {
if (val) { s->cr = (s->cr & ~R_CR_HSERDY_MASK) |
clock_update_hz(s->hse, s->hse_frequency); (val << R_CR_HSERDY_SHIFT);
if (s->cier & R_CIER_HSERDYIE_MASK) { if (val) {
s->cifr |= R_CIFR_HSERDYF_MASK; clock_update_hz(s->hse, s->hse_frequency);
if (s->cier & R_CIER_HSERDYIE_MASK) {
s->cifr |= R_CIFR_HSERDYF_MASK;
}
} else {
clock_update(s->hse, 0);
} }
} else { } else {
clock_update(s->hse, 0); s->cr |= R_CR_HSEON_MASK;
} }
/* HSIAFS: TODO*/ /* HSIAFS: TODO*/
/* HSIKERON: TODO*/ /* HSIKERON: TODO*/
/* HSION and update HSIRDY*/ /*
val = FIELD_EX32(s->cr, CR, HSION); * HSION and update HSIRDY
s->cr = (s->cr & ~R_CR_HSIRDY_MASK) | * HSION is set by hardware if the HSI16 is used directly
(val << R_CR_HSIRDY_SHIFT); * or indirectly as system clock.
if (val) { */
if (FIELD_EX32(s->cfgr, CFGR, SWS) == 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); clock_update_hz(s->hsi16_rc, HSI_FRQ);
if (s->cier & R_CIER_HSIRDYIE_MASK) { if (s->cier & R_CIER_HSIRDYIE_MASK) {
s->cifr |= R_CIFR_HSIRDYF_MASK; s->cifr |= R_CIFR_HSIRDYF_MASK;
} }
} else { } else {
clock_update(s->hsi16_rc, 0); val = FIELD_EX32(s->cr, CR, HSION);
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(s->hsi16_rc, 0);
s->cr &= ~R_CR_HSIRDY_MASK;
}
} }
static const uint32_t msirange[] = { /* MSIPLLEN: TODO */
100000, 200000, 400000, 800000, 1000000, 2000000,
4000000, 8000000, 16000000, 24000000, 32000000, 48000000 /*
}; * MSION and update MSIRDY
/* MSIRANGE and MSIRGSEL */ * Set by hardware when used directly or indirectly as system clock.
val = FIELD_EX32(s->cr, CR, MSIRGSEL); */
if (val) { if (FIELD_EX32(s->cfgr, CFGR, SWS) == 0b00 ||
/* MSIRGSEL is set, use the MSIRANGE field */ current_pll_src == RCC_CLOCK_MUX_SRC_MSI) {
val = FIELD_EX32(s->cr, CR, MSIRANGE); 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 { } else {
/* MSIRGSEL is not set, use the MSISRANGE field */ val = FIELD_EX32(s->cr, CR, MSION);
val = FIELD_EX32(s->csr, CSR, MSISRANGE); if (val) {
} s->cr |= R_CR_MSIRDY_MASK;
rcc_update_msi(s, previous_value);
if (val < ARRAY_SIZE(msirange)) { if (s->cier & R_CIER_MSIRDYIE_MASK) {
clock_update_hz(s->msi_rc, msirange[val]); s->cifr |= R_CIFR_MSIRDYF_MASK;
} else { }
clock_update_hz(s->msi_rc, MSI_DEFAULT_FRQ); } else {
/* TODO: there is a write protection if the value is out of bound, s->cr &= ~R_CR_MSIRDY_MASK;
implement that instead of setting the default */ clock_update(s->msi_rc, 0);
} }
/* MSIPLLEN */
/* MSION and update MSIRDY */
val = FIELD_EX32(s->cr, CR, MSION);
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;
} }
rcc_update_irq(s); rcc_update_irq(s);
} }
@ -991,15 +1053,17 @@ static void stm32l4x5_rcc_write(void *opaque, hwaddr addr,
uint64_t val64, unsigned int size) uint64_t val64, unsigned int size)
{ {
Stm32l4x5RccState *s = opaque; Stm32l4x5RccState *s = opaque;
uint32_t previous_value = 0;
const uint32_t value = val64; const uint32_t value = val64;
trace_stm32l4x5_rcc_write(addr, value); trace_stm32l4x5_rcc_write(addr, value);
switch (addr) { switch (addr) {
case A_CR: case A_CR:
previous_value = s->cr;
s->cr = (s->cr & CR_READ_SET_MASK) | s->cr = (s->cr & CR_READ_SET_MASK) |
(value & (CR_READ_SET_MASK | ~CR_READ_ONLY_MASK)); (value & (CR_READ_SET_MASK | ~CR_READ_ONLY_MASK));
rcc_update_cr_register(s); rcc_update_cr_register(s, previous_value);
break; break;
case A_ICSCR: case A_ICSCR:
s->icscr = value & ~ICSCR_READ_ONLY_MASK; s->icscr = value & ~ICSCR_READ_ONLY_MASK;