diff --git a/arch/arm/soc/st_stm32/stm32f4/flash_map.h b/arch/arm/soc/st_stm32/stm32f4/flash_map.h new file mode 100644 index 00000000000..14c5f979b94 --- /dev/null +++ b/arch/arm/soc/st_stm32/stm32f4/flash_map.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 Linaro Limited. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _STM32F4X_FLASH_MAP_H_ +#define _STM32F4X_FLASH_MAP_H_ + +#include "sys/types.h" + +struct stm32f4x_flash_sector { + const off_t start; + const off_t end; +}; + +#define STM32F4X_FLASH_SECTOR(offset, bytes) \ + { \ + .start = offset, \ + .end = offset + bytes - 1, \ + } + +__attribute__((unused)) +struct stm32f4x_flash_sector stm32f4xx_sectors[] = { + STM32F4X_FLASH_SECTOR(0x00000, KB(16)), + STM32F4X_FLASH_SECTOR(0x04000, KB(16)), + STM32F4X_FLASH_SECTOR(0x08000, KB(16)), + STM32F4X_FLASH_SECTOR(0x0c000, KB(16)), + STM32F4X_FLASH_SECTOR(0x10000, KB(64)), + STM32F4X_FLASH_SECTOR(0x20000, KB(128)), + STM32F4X_FLASH_SECTOR(0x40000, KB(128)), +#ifdef CONFIG_SOC_STM32F401XE + STM32F4X_FLASH_SECTOR(0x60000, KB(128)), +#endif +}; + +#define STM32F4X_FLASH_TIMEOUT ((uint32_t) 0x000B0000) +#define STM32F4X_SECTOR_MASK ((uint32_t) 0xFFFFFF07) +#define STM32F4X_SECTORS ARRAY_SIZE(stm32f4xx_sectors) + +#define STM32F4X_FLASH_END \ + (stm32f4xx_sectors[ARRAY_SIZE(stm32f4xx_sectors) - 1].end) + +int stm32f4x_get_sector(off_t offset) +{ + int i; + + for (i = 0; i < STM32F4X_SECTORS; i++) { + if (offset > stm32f4xx_sectors[i].end) { + continue; + } + break; + } + + return i; +} + +#endif /* _STM32F4X_FLASHMAP_H_ */ diff --git a/arch/arm/soc/st_stm32/stm32f4/flash_registers.h b/arch/arm/soc/st_stm32/stm32f4/flash_registers.h index a0dea61c83c..6ede71b907c 100644 --- a/arch/arm/soc/st_stm32/stm32f4/flash_registers.h +++ b/arch/arm/soc/st_stm32/stm32f4/flash_registers.h @@ -40,12 +40,12 @@ union __flash_acr { /* 3.8.7 Embedded flash registers */ struct stm32f4x_flash { - union __flash_acr acr; - uint32_t key; - uint32_t optkey; + volatile union __flash_acr acr; + volatile uint32_t key; + volatile uint32_t optkey; volatile uint32_t status; volatile uint32_t ctrl; - uint32_t optctrl; + volatile uint32_t optctrl; }; /** diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index 07f4c24fe44..2450bdda6bd 100644 --- a/drivers/flash/Kconfig +++ b/drivers/flash/Kconfig @@ -148,4 +148,4 @@ config SOC_FLASH_MCUX_DEV_NAME help Specify the device name for the flash driver. -source "drivers/flash/Kconfig.stm32f3x" +source "drivers/flash/Kconfig.stm32fxx" diff --git a/drivers/flash/Kconfig.stm32f3x b/drivers/flash/Kconfig.stm32fxx similarity index 72% rename from drivers/flash/Kconfig.stm32f3x rename to drivers/flash/Kconfig.stm32fxx index 6e5e375a060..a58c05cefd4 100644 --- a/drivers/flash/Kconfig.stm32f3x +++ b/drivers/flash/Kconfig.stm32fxx @@ -5,15 +5,15 @@ # SPDX-License-Identifier: Apache-2.0 # -if FLASH && SOC_SERIES_STM32F3X +if FLASH && SOC_FAMILY_STM32 menuconfig SOC_FLASH_STM32 bool prompt "STM32 flash driver" - depends on SOC_SERIES_STM32F3X && FLASH - default n + depends on (SOC_SERIES_STM32F3X || SOC_SERIES_STM32F4X) + default y help - Enable STM32F3X series MCUs flash driver. + Enable STM32F3x OR STM32F4x series flash driver. config SOC_FLASH_STM32_DEV_NAME string "STM32 flash device name" diff --git a/drivers/flash/Makefile b/drivers/flash/Makefile index 5009e6e25a3..beb28f2c91d 100644 --- a/drivers/flash/Makefile +++ b/drivers/flash/Makefile @@ -7,3 +7,7 @@ ifeq ($(CONFIG_SOC_SERIES_STM32F3X),y) obj-$(CONFIG_SOC_FLASH_STM32) += flash_stm32f3x.o obj-$(CONFIG_SOC_FLASH_STM32) += flash_stm32f3x_priv.o endif + +ifeq ($(CONFIG_SOC_SERIES_STM32F4X),y) +obj-$(CONFIG_SOC_FLASH_STM32) += flash_stm32f4x.o +endif diff --git a/drivers/flash/flash_stm32f4x.c b/drivers/flash/flash_stm32f4x.c new file mode 100644 index 00000000000..d31fb210bbc --- /dev/null +++ b/drivers/flash/flash_stm32f4x.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2017 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct flash_priv { + struct stm32f4x_flash *regs; + struct k_sem sem; +}; + +static bool valid_range(off_t offset, uint32_t len) +{ + return offset >= 0 && (offset + len - 1 <= STM32F4X_FLASH_END); +} + +static int check_status(struct stm32f4x_flash *regs) +{ + uint32_t const error = + FLASH_FLAG_WRPERR | + FLASH_FLAG_PGAERR | + FLASH_FLAG_RDERR | + FLASH_FLAG_PGPERR | + FLASH_FLAG_PGSERR | + FLASH_FLAG_OPERR; + + if (regs->status & error) { + return -EIO; + } + + return 0; +} + +static int wait_flash_idle(struct stm32f4x_flash *regs) +{ + uint32_t timeout = STM32F4X_FLASH_TIMEOUT; + int rc; + + rc = check_status(regs); + if (rc < 0) { + return -EIO; + } + + while ((regs->status & FLASH_FLAG_BSY) && timeout) { + timeout--; + } + + if (!timeout) { + return -EIO; + } + + return 0; +} + +static int write_byte(off_t offset, uint8_t val, struct stm32f4x_flash *regs) +{ + uint32_t tmp; + int rc; + + /* if the control register is locked, do not fail silently */ + if (regs->ctrl & FLASH_CR_LOCK) { + return -EIO; + } + + rc = wait_flash_idle(regs); + if (rc < 0) { + return rc; + } + + regs->ctrl &= CR_PSIZE_MASK; + regs->ctrl |= FLASH_PSIZE_BYTE; + regs->ctrl |= FLASH_CR_PG; + + /* flush the register write */ + tmp = regs->ctrl; + + *((uint8_t *) offset + CONFIG_FLASH_BASE_ADDRESS) = val; + + rc = wait_flash_idle(regs); + regs->ctrl &= (~FLASH_CR_PG); + + return rc; +} + +static int erase_sector(uint16_t sector, struct stm32f4x_flash *regs) +{ + uint32_t tmp; + int rc; + + /* if the control register is locked, do not fail silently */ + if (regs->ctrl & FLASH_CR_LOCK) { + return -EIO; + } + + rc = wait_flash_idle(regs); + if (rc < 0) { + return rc; + } + + regs->ctrl &= STM32F4X_SECTOR_MASK; + regs->ctrl |= FLASH_CR_SER | (sector << 3); + regs->ctrl |= FLASH_CR_STRT; + + /* flush the register write */ + tmp = regs->ctrl; + + rc = wait_flash_idle(regs); + regs->ctrl &= (FLASH_CR_SER | FLASH_CR_SNB); + + return rc; +} + +static void flush_caches(struct stm32f4x_flash *regs) +{ + if (regs->acr.val & FLASH_ACR_ICEN) { + regs->acr.val &= ~FLASH_ACR_ICEN; + regs->acr.val |= FLASH_ACR_ICRST; + regs->acr.val &= ~FLASH_ACR_ICRST; + regs->acr.val |= FLASH_ACR_ICEN; + } + + if (regs->acr.val & FLASH_ACR_DCEN) { + regs->acr.val &= ~FLASH_ACR_DCEN; + regs->acr.val |= FLASH_ACR_DCRST; + regs->acr.val &= ~FLASH_ACR_DCRST; + regs->acr.val |= FLASH_ACR_DCEN; + } +} + +static int flash_stm32f4x_erase(struct device *dev, off_t offset, size_t len) +{ + struct flash_priv *p = dev->driver_data; + int i, rc = 0; + + if (!valid_range(offset, len)) { + return -EINVAL; + } + + if (!len) { + return 0; + } + + k_sem_take(&p->sem, K_FOREVER); + + i = stm32f4x_get_sector(offset); + for (; i <= stm32f4x_get_sector(offset + len - 1); i++) { + rc = erase_sector(i, p->regs); + if (rc < 0) { + break; + } + } + flush_caches(p->regs); + + k_sem_give(&p->sem); + + return rc; +} + +static int flash_stm32f4x_read(struct device *dev, off_t offset, void *data, + size_t len) +{ + if (!valid_range(offset, len)) { + return -EINVAL; + } + + if (!len) { + return 0; + } + + memcpy(data, (void *) CONFIG_FLASH_BASE_ADDRESS + offset, len); + + return 0; +} + +static int flash_stm32f4x_write(struct device *dev, off_t offset, + const void *data, size_t len) +{ + struct flash_priv *p = dev->driver_data; + int rc, i; + + if (!valid_range(offset, len)) { + return -EINVAL; + } + + if (!len) { + return 0; + } + + k_sem_take(&p->sem, K_FOREVER); + + for (i = 0; i < len; i++, offset++) { + rc = write_byte(offset, ((const uint8_t *) data)[i], p->regs); + if (rc < 0) { + k_sem_give(&p->sem); + return rc; + } + } + + k_sem_give(&p->sem); + + return 0; +} + +static int flash_stm32f4x_write_protection(struct device *dev, bool enable) +{ + struct flash_priv *p = dev->driver_data; + struct stm32f4x_flash *regs = p->regs; + int rc = 0; + + k_sem_take(&p->sem, K_FOREVER); + + if (enable) { + rc = wait_flash_idle(regs); + if (rc) { + k_sem_give(&p->sem); + return rc; + } + regs->ctrl |= FLASH_CR_LOCK; + } else { + if (regs->ctrl & FLASH_CR_LOCK) { + regs->key = FLASH_KEY1; + regs->key = FLASH_KEY2; + } + } + + k_sem_give(&p->sem); + + return rc; +} + +static struct flash_priv flash_data = { + .regs = (struct stm32f4x_flash *) FLASH_R_BASE, +}; + +static const struct flash_driver_api flash_stm32f4x_api = { + .write_protection = flash_stm32f4x_write_protection, + .erase = flash_stm32f4x_erase, + .write = flash_stm32f4x_write, + .read = flash_stm32f4x_read, +}; + +static int stm32f4x_flash_init(struct device *dev) +{ + struct flash_priv *p = dev->driver_data; + + k_sem_init(&p->sem, 1, 1); + + return flash_stm32f4x_write_protection(dev, false); +} + +DEVICE_AND_API_INIT(stm32f4x_flash, + CONFIG_SOC_FLASH_STM32_DEV_NAME, + stm32f4x_flash_init, + &flash_data, + NULL, + POST_KERNEL, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &flash_stm32f4x_api); +