diff --git a/drivers/flash/Kconfig.stm32 b/drivers/flash/Kconfig.stm32 index 55aa1c7dcca44..f183f45fec93b 100644 --- a/drivers/flash/Kconfig.stm32 +++ b/drivers/flash/Kconfig.stm32 @@ -14,7 +14,6 @@ config SOC_FLASH_STM32 default y select FLASH_PAGE_LAYOUT select FLASH_HAS_PAGE_LAYOUT - select FLASH_HAS_EX_OP if SOC_SERIES_STM32F4X select MPU_ALLOW_FLASH_WRITE if ARM_MPU help Enable flash driver for STM32 series @@ -24,6 +23,7 @@ if SOC_FLASH_STM32 config FLASH_STM32_WRITE_PROTECT bool "Extended operation for flash write protection control" depends on SOC_SERIES_STM32F4X + select FLASH_HAS_EX_OP default n help Enables flash extended operation for enabling/disabling flash write @@ -39,7 +39,8 @@ config FLASH_STM32_WRITE_PROTECT_DISABLE_PREVENTION config FLASH_STM32_READOUT_PROTECTION bool "Extended operation for flash readout protection control" - depends on SOC_SERIES_STM32F4X + depends on SOC_SERIES_STM32F4X || SOC_SERIES_STM32L4X + select FLASH_HAS_EX_OP default n help Enables flash extended operation for enabling/disabling flash readout diff --git a/drivers/flash/flash_stm32.c b/drivers/flash/flash_stm32.c index cba4a76a96905..36e3103c56d39 100644 --- a/drivers/flash/flash_stm32.c +++ b/drivers/flash/flash_stm32.c @@ -390,7 +390,7 @@ int flash_stm32_option_bytes_lock(const struct device *dev, bool enable) return 0; } -#if defined(CONFIG_FLASH_STM32_BLOCK_REGISTERS) +#if defined(CONFIG_FLASH_EX_OP_ENABLED) && defined(CONFIG_FLASH_STM32_BLOCK_REGISTERS) static int flash_stm32_control_register_disable(const struct device *dev) { FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); diff --git a/drivers/flash/flash_stm32f4x.c b/drivers/flash/flash_stm32f4x.c index f94c1b2e97936..4261c26a58902 100644 --- a/drivers/flash/flash_stm32f4x.c +++ b/drivers/flash/flash_stm32f4x.c @@ -292,8 +292,7 @@ int flash_stm32_update_rdp(const struct device *dev, bool enable, switch (current_level) { case FLASH_STM32_RDP2: if (!enable || !permanent) { - __ASSERT(false, "RDP level 2 is permanent and can't be " - "changed!"); + LOG_ERR("RDP level 2 is permanent and can't be changed!"); return -ENOTSUP; } break; @@ -304,9 +303,8 @@ int flash_stm32_update_rdp(const struct device *dev, bool enable, #if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW) target_level = FLASH_STM32_RDP2; #else - __ASSERT(false, - "Permanent readout protection (RDP " - "level 0 -> 2) not allowed"); + LOG_ERR("Permanent readout protection (RDP " + "level 0 -> 2) not allowed"); return -ENOTSUP; #endif } @@ -317,8 +315,8 @@ int flash_stm32_update_rdp(const struct device *dev, bool enable, #if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW) target_level = FLASH_STM32_RDP2; #else - __ASSERT(false, "Permanent readout protection (RDP " - "level 1 -> 2) not allowed"); + LOG_ERR("Permanent readout protection (RDP " + "level 1 -> 2) not allowed"); return -ENOTSUP; #endif } @@ -326,8 +324,8 @@ int flash_stm32_update_rdp(const struct device *dev, bool enable, #if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_DISABLE_ALLOW) target_level = FLASH_STM32_RDP0; #else - __ASSERT(false, "Disabling readout protection (RDP " - "level 1 -> 0) not allowed"); + LOG_ERR("Disabling readout protection (RDP " + "level 1 -> 0) not allowed"); return -EACCES; #endif } diff --git a/drivers/flash/flash_stm32l4x.c b/drivers/flash/flash_stm32l4x.c index 24644aba95e49..ead57fb3df41c 100644 --- a/drivers/flash/flash_stm32l4x.c +++ b/drivers/flash/flash_stm32l4x.c @@ -15,6 +15,7 @@ LOG_MODULE_REGISTER(LOG_DOMAIN); #include #include #include +#include #include #include @@ -244,6 +245,157 @@ int flash_stm32_write_range(const struct device *dev, unsigned int offset, return rc; } +static __unused int write_optb(const struct device *dev, uint32_t mask, + uint32_t value) +{ + FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); + int rc; + + if (regs->CR & FLASH_CR_OPTLOCK) { + return -EIO; + } + + if ((regs->OPTR & mask) == value) { + return 0; + } + + rc = flash_stm32_wait_flash_idle(dev); + if (rc < 0) { + return rc; + } + + regs->OPTR = (regs->OPTR & ~mask) | value; + regs->CR |= FLASH_CR_OPTSTRT; + + /* Make sure previous write is completed. */ + barrier_dsync_fence_full(); + + rc = flash_stm32_wait_flash_idle(dev); + if (rc < 0) { + return rc; + } + + return 0; +} + +#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) + +/* + * Remark for future development implementing Write Protection for the L4 parts: + * + * STM32L4 allows for 2 write protected memory areas, c.f. FLASH_WEP1AR, FLASH_WRP1BR + * which are defined by their start and end pages. + * + * Other STM32 parts (i.e. F4 series) uses bitmask to select sectors. + * + * To implement Write Protection for L4 one should thus add a new EX_OP like + * FLASH_STM32_EX_OP_SECTOR_WP_RANGED in stm32_flash_api_extensions.h + */ + +#endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */ + +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION) +int flash_stm32_update_rdp(const struct device *dev, bool enable, + bool permanent) +{ + FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); + uint8_t current_level, target_level; + + current_level = + (regs->OPTR & FLASH_OPTR_RDP_Msk) >> FLASH_OPTR_RDP_Pos; + target_level = current_level; + + /* + * 0xAA = RDP level 0 (no protection) + * 0xCC = RDP level 2 (permanent protection) + * others = RDP level 1 (protection active) + */ + switch (current_level) { + case FLASH_STM32_RDP2: + if (!enable || !permanent) { + LOG_ERR("RDP level 2 is permanent and can't be changed!"); + return -ENOTSUP; + } + break; + case FLASH_STM32_RDP0: + if (enable) { + target_level = FLASH_STM32_RDP1; + if (permanent) { +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW) + target_level = FLASH_STM32_RDP2; +#else + LOG_ERR("Permanent readout protection (RDP " + "level 0 -> 2) not allowed"); + return -ENOTSUP; +#endif + } + } + break; + default: /* FLASH_STM32_RDP1 */ + if (enable && permanent) { +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW) + target_level = FLASH_STM32_RDP2; +#else + LOG_ERR("Permanent readout protection (RDP " + "level 1 -> 2) not allowed"); + return -ENOTSUP; +#endif + } + if (!enable) { +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_DISABLE_ALLOW) + target_level = FLASH_STM32_RDP0; +#else + LOG_ERR("Disabling readout protection (RDP " + "level 1 -> 0) not allowed"); + return -EACCES; +#endif + } + } + + /* Update RDP level if needed */ + if (current_level != target_level) { + LOG_INF("RDP changed from 0x%02x to 0x%02x", current_level, + target_level); + + write_optb(dev, FLASH_OPTR_RDP_Msk, + (uint32_t)target_level << FLASH_OPTR_RDP_Pos); + } + return 0; +} + +int flash_stm32_get_rdp(const struct device *dev, bool *enabled, + bool *permanent) +{ + FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); + uint8_t current_level; + + current_level = + (regs->OPTR & FLASH_OPTR_RDP_Msk) >> FLASH_OPTR_RDP_Pos; + + /* + * 0xAA = RDP level 0 (no protection) + * 0xCC = RDP level 2 (permanent protection) + * others = RDP level 1 (protection active) + */ + switch (current_level) { + case FLASH_STM32_RDP2: + *enabled = true; + *permanent = true; + break; + case FLASH_STM32_RDP0: + *enabled = false; + *permanent = false; + break; + default: /* FLASH_STM32_RDP1 */ + *enabled = true; + *permanent = false; + } + return 0; +} +#endif /* CONFIG_FLASH_STM32_READOUT_PROTECTION */ + + + void flash_stm32_page_layout(const struct device *dev, const struct flash_pages_layout **layout, size_t *layout_size) diff --git a/tests/drivers/flash/stm32/prj.conf b/tests/drivers/flash/stm32/prj.conf index 4f43063f0cd7c..68d772c1946c1 100644 --- a/tests/drivers/flash/stm32/prj.conf +++ b/tests/drivers/flash/stm32/prj.conf @@ -4,8 +4,6 @@ CONFIG_ZTEST_NEW_API=y CONFIG_FLASH=y CONFIG_FLASH_EX_OP_ENABLED=y -CONFIG_FLASH_STM32_READOUT_PROTECTION=y -CONFIG_FLASH_STM32_WRITE_PROTECT=y CONFIG_FLASH_STM32_BLOCK_REGISTERS=y CONFIG_MAIN_STACK_SIZE=2048 diff --git a/tests/drivers/flash/stm32/src/main.c b/tests/drivers/flash/stm32/src/main.c index 9d8ae0acc5b0f..d4d0bf1c201e8 100644 --- a/tests/drivers/flash/stm32/src/main.c +++ b/tests/drivers/flash/stm32/src/main.c @@ -20,6 +20,8 @@ #define EXPECTED_SIZE 512 static const struct device *const flash_dev = TEST_AREA_DEVICE; + +#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) static const struct flash_parameters *flash_params; static uint32_t sector_mask; static uint8_t __aligned(4) expected[EXPECTED_SIZE]; @@ -39,14 +41,17 @@ static int sector_mask_from_offset(const struct device *dev, off_t offset, return 0; } +#endif static void *flash_stm32_setup(void) { +#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) struct flash_stm32_ex_op_sector_wp_out wp_status; struct flash_stm32_ex_op_sector_wp_in wp_request; uint8_t buf[EXPECTED_SIZE]; bool is_buf_clear = true; int rc; +#endif /* Check if tested region fits in flash. */ zassert_true((TEST_AREA_OFFSET + EXPECTED_SIZE) < TEST_AREA_MAX, @@ -54,6 +59,7 @@ static void *flash_stm32_setup(void) zassert_true(device_is_ready(flash_dev)); +#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) flash_params = flash_get_parameters(flash_dev); rc = sector_mask_from_offset(flash_dev, TEST_AREA_OFFSET, EXPECTED_SIZE, @@ -102,10 +108,12 @@ static void *flash_stm32_setup(void) for (int i = 0; i < EXPECTED_SIZE; i++) { expected[i] = i; } +#endif return NULL; } +#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) ZTEST(flash_stm32, test_stm32_write_protection) { struct flash_stm32_ex_op_sector_wp_in wp_request; @@ -153,7 +161,9 @@ ZTEST(flash_stm32, test_stm32_write_protection) zassert_equal(memcmp(buf, expected, EXPECTED_SIZE), 0, "Read data doesn't match expected data"); } +#endif +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION) ZTEST(flash_stm32, test_stm32_readout_protection_disabled) { struct flash_stm32_ex_op_rdp rdp_status; @@ -167,5 +177,6 @@ ZTEST(flash_stm32, test_stm32_readout_protection_disabled) TC_PRINT("RDP is disabled\n"); } +#endif ZTEST_SUITE(flash_stm32, NULL, flash_stm32_setup, NULL, NULL, NULL); diff --git a/tests/drivers/flash/stm32/testcase.yaml b/tests/drivers/flash/stm32/testcase.yaml index 9fcfa10274476..50014898591f5 100644 --- a/tests/drivers/flash/stm32/testcase.yaml +++ b/tests/drivers/flash/stm32/testcase.yaml @@ -3,9 +3,20 @@ common: - drivers - flash tests: - drivers.flash.stm32.default: + drivers.flash.stm32.f4: platform_allow: - nucleo_f429zi - google_dragonclaw - filter: dt_compat_enabled("st,stm32-flash-controller") and + extra_configs: + - CONFIG_FLASH_STM32_WRITE_PROTECT=y + - CONFIG_FLASH_STM32_READOUT_PROTECTION=y + filter: dt_compat_enabled("st,stm32f4-flash-controller") and + dt_label_with_parent_compat_enabled("storage_partition", "fixed-partitions") + drivers.flash.stm32.l4: + platform_allow: + - nucleo_l452re_p + - disco_l475_iot1 + extra_configs: + - CONFIG_FLASH_STM32_READOUT_PROTECTION=y + filter: dt_compat_enabled("st,stm32l4-flash-controller") and dt_label_with_parent_compat_enabled("storage_partition", "fixed-partitions")