drivers: flash: stm32h7: add support for blocking registers

Add support for blocking flash control registers and option bytes for
STM32H7 chips.

Signed-off-by: Dawid Niedzwiecki <dawidn@google.com>
This commit is contained in:
Dawid Niedzwiecki 2024-12-20 13:31:20 +01:00 committed by Benjamin Cabé
parent def973f5b9
commit 3d372c048a
3 changed files with 113 additions and 5 deletions

View File

@ -242,6 +242,70 @@ int flash_stm32_get_wp_sectors(const struct device *dev, uint64_t *protected_sec
}
#endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */
#ifdef CONFIG_FLASH_STM32_BLOCK_REGISTERS
int flash_stm32_control_register_disable(const struct device *dev)
{
FLASH_TypeDef *regs = FLASH_STM32_REGS(dev);
/*
* Access to control register can be disabled by writing wrong key to
* the key register. Option register will remain disabled until reset.
* Writing wrong key causes a bus fault, so we need to set FAULTMASK to
* disable faults, and clear bus fault pending bit before enabling them
* again.
*/
regs->CR1 |= FLASH_CR_LOCK;
#ifdef DUAL_BANK
regs->CR2 |= FLASH_CR_LOCK;
#endif /* DUAL_BANK */
__set_FAULTMASK(1);
regs->KEYR1 = 0xffffffff;
#ifdef DUAL_BANK
regs->KEYR2 = 0xffffffff;
#endif /* DUAL_BANK */
/* Make sure that the fault occurs before we clear it. */
barrier_dsync_fence_full();
/* Clear Bus Fault pending bit */
SCB->SHCSR &= ~SCB_SHCSR_BUSFAULTPENDED_Msk;
/* Make sure to clear the fault before changing the fault mask. */
barrier_dsync_fence_full();
__set_FAULTMASK(0);
return 0;
}
int flash_stm32_option_bytes_disable(const struct device *dev)
{
FLASH_TypeDef *regs = FLASH_STM32_REGS(dev);
/*
* Access to option register can be disabled by writing wrong key to
* the key register. Option register will remain disabled until reset.
* Writing wrong key causes a bus fault, so we need to set FAULTMASK to
* disable faults, and clear bus fault pending bit before enabling them
* again.
*/
regs->OPTCR |= FLASH_OPTCR_OPTLOCK;
__set_FAULTMASK(1);
regs->OPTKEYR = 0xffffffff;
/* Make sure that the fault occurs before we clear it. */
barrier_dsync_fence_full();
/* Clear Bus Fault pending bit */
SCB->SHCSR &= ~SCB_SHCSR_BUSFAULTPENDED_Msk;
/* Make sure to clear the fault before changing the fault mask. */
barrier_dsync_fence_full();
__set_FAULTMASK(0);
return 0;
}
#endif /* CONFIG_FLASH_STM32_BLOCK_REGISTERS */
int flash_stm32_option_bytes_lock(const struct device *dev, bool enable)
{
FLASH_TypeDef *regs = FLASH_STM32_REGS(dev);

View File

@ -10,6 +10,7 @@
#include <zephyr/drivers/flash/stm32_flash_api_extensions.h>
#include <zephyr/devicetree.h>
#include <zephyr/storage/flash_map.h>
#include <zephyr/sys/barrier.h>
#define TEST_AREA storage_partition
#define TEST_AREA_OFFSET FIXED_PARTITION_OFFSET(TEST_AREA)
@ -200,15 +201,46 @@ static void flash_cr_unlock(void)
{
FLASH_TypeDef *regs = (FLASH_TypeDef *)TEST_AREA_DEVICE_REG;
#ifdef CONFIG_SOC_SERIES_STM32H7X
regs->KEYR1 = FLASH_KEY1;
regs->KEYR1 = FLASH_KEY2;
#ifdef DUAL_BANK
regs->KEYR2 = FLASH_KEY1;
regs->KEYR2 = FLASH_KEY2;
#endif /* DUAL_BANK */
#else /* CONFIG_SOC_SERIES_STM32H7X */
regs->KEYR = FLASH_KEY1;
regs->KEYR = FLASH_KEY2;
#endif /* CONFIG_SOC_SERIES_STM32H7X */
barrier_dsync_fence_full();
}
static bool flash_cr_locked(void)
static bool flash_cr_is_locked(void)
{
FLASH_TypeDef *regs = (FLASH_TypeDef *)TEST_AREA_DEVICE_REG;
#ifdef CONFIG_SOC_SERIES_STM32H7X
return regs->CR1 & FLASH_CR_LOCK;
#ifdef DUAL_BANK
return (regs->CR1 & FLASH_CR_LOCK) && (regs->CR2 & FLASH_CR_LOCK);
#endif /* DUAL_BANK */
#else /* CONFIG_SOC_SERIES_STM32H7X */
return regs->CR & FLASH_CR_LOCK;
#endif /* CONFIG_SOC_SERIES_STM32H7X */
}
static bool flash_cr_is_unlocked(void)
{
FLASH_TypeDef *regs = (FLASH_TypeDef *)TEST_AREA_DEVICE_REG;
#ifdef CONFIG_SOC_SERIES_STM32H7X
return !(regs->CR1 & FLASH_CR_LOCK);
#ifdef DUAL_BANK
return !((regs->CR1 & FLASH_CR_LOCK) || (regs->CR2 & FLASH_CR_LOCK));
#endif /* DUAL_BANK */
#else /* CONFIG_SOC_SERIES_STM32H7X */
return !(regs->CR & FLASH_CR_LOCK);
#endif /* CONFIG_SOC_SERIES_STM32H7X */
}
ZTEST(flash_stm32, test_stm32_block_registers)
@ -227,24 +259,27 @@ ZTEST(flash_stm32, test_stm32_block_registers)
flash_stm32_option_bytes_lock(flash_dev, false);
/* Clear Bus Fault pending bit */
SCB->SHCSR &= ~SCB_SHCSR_BUSFAULTPENDED_Msk;
barrier_dsync_fence_full();
__set_FAULTMASK(0);
zassert_true(flash_opt_locked(), "OPT unlocked after being blocked");
/* Test CR lock. */
zassert_true(flash_cr_locked(), "CR should be locked by default");
zassert_true(flash_cr_is_locked(), "CR should be locked by default");
TC_PRINT("Unlocking CR\n");
flash_cr_unlock();
zassert_false(flash_cr_locked(), "Unable to unlock CR");
zassert_true(flash_cr_is_unlocked(), "Unable to unlock CR");
TC_PRINT("Blocking CR\n");
flash_ex_op(flash_dev, FLASH_STM32_EX_OP_BLOCK_CONTROL_REG, (uintptr_t)NULL, NULL);
zassert_true(flash_cr_locked(), "Blocking CR didn't lock CR");
zassert_true(flash_cr_is_locked(), "Blocking CR didn't lock CR");
__set_FAULTMASK(1);
TC_PRINT("Try to unlock blocked CR\n");
flash_cr_unlock();
/* Clear Bus Fault pending bit */
SCB->SHCSR &= ~SCB_SHCSR_BUSFAULTPENDED_Msk;
barrier_dsync_fence_full();
__set_FAULTMASK(0);
zassert_true(flash_cr_locked(), "CR unlocked after being blocked");
/* Make sure previous write is completed. */
zassert_true(flash_cr_is_locked(), "CR unlocked after being blocked");
}
#endif

View File

@ -54,3 +54,12 @@ tests:
- CONFIG_FLASH_STM32_WRITE_PROTECT=y
filter: dt_compat_enabled("st,stm32h7-flash-controller") and
dt_label_with_parent_compat_enabled("storage_partition", "fixed-partitions")
drivers.flash.stm32.h7.block_registers:
platform_allow:
- nucleo_h743zi
- google_icetower
extra_configs:
- CONFIG_FLASH_STM32_BLOCK_REGISTERS=y
filter: dt_compat_enabled("st,stm32h7-flash-controller") and
dt_label_with_parent_compat_enabled("storage_partition", "fixed-partitions") and
CONFIG_SOC_SERIES_STM32H7X