diff --git a/arch/arm/soc/st_stm32/stm32f4/flash_map.h b/arch/arm/soc/st_stm32/stm32f4/flash_map.h index aee0ec52a78ced..97ac0da9781dc2 100644 --- a/arch/arm/soc/st_stm32/stm32f4/flash_map.h +++ b/arch/arm/soc/st_stm32/stm32f4/flash_map.h @@ -20,8 +20,8 @@ struct stm32f4x_flash_sector { .end = offset + bytes - 1, \ } -__attribute__((unused)) -struct stm32f4x_flash_sector stm32f4xx_sectors[] = { +__unused +static struct stm32f4x_flash_sector stm32f4xx_sectors[] = { STM32F4X_FLASH_SECTOR(0x00000, KB(16)), STM32F4X_FLASH_SECTOR(0x04000, KB(16)), STM32F4X_FLASH_SECTOR(0x08000, KB(16)), @@ -41,7 +41,8 @@ struct stm32f4x_flash_sector stm32f4xx_sectors[] = { #define STM32F4X_FLASH_END \ (stm32f4xx_sectors[ARRAY_SIZE(stm32f4xx_sectors) - 1].end) -int stm32f4x_get_sector(off_t offset) +__unused +static int stm32f4x_get_sector(off_t offset) { int i; diff --git a/arch/arm/soc/st_stm32/stm32f4/flash_registers.h b/arch/arm/soc/st_stm32/stm32f4/flash_registers.h index 67c88734841b90..eef7a8fafa02e5 100644 --- a/arch/arm/soc/st_stm32/stm32f4/flash_registers.h +++ b/arch/arm/soc/st_stm32/stm32f4/flash_registers.h @@ -32,11 +32,11 @@ union __flash_acr { /* 3.8.7 Embedded flash registers */ struct stm32f4x_flash { volatile union __flash_acr acr; - volatile u32_t key; - volatile u32_t optkey; - volatile u32_t status; - volatile u32_t ctrl; - volatile u32_t optctrl; + volatile u32_t keyr; + volatile u32_t optkeyr; + volatile u32_t sr; + volatile u32_t cr; + volatile u32_t optcr; }; #endif /* _STM32F4X_FLASHREGISTERS_H_ */ diff --git a/arch/arm/soc/st_stm32/stm32l4/flash_registers.h b/arch/arm/soc/st_stm32/stm32l4/flash_registers.h index ef1fa0c401fc22..f63e0e5fe27e41 100644 --- a/arch/arm/soc/st_stm32/stm32l4/flash_registers.h +++ b/arch/arm/soc/st_stm32/stm32l4/flash_registers.h @@ -35,29 +35,29 @@ union __ef_acr { /* FLASH register map */ struct stm32l4x_flash { - union __ef_acr acr; - u32_t pdkeyr; - u32_t keyr; - u32_t optkeyr; - u32_t sr; - u32_t cr; - u32_t eccr; - u32_t rsvd_0; - u32_t optr; - u32_t pcrop1sr; - u32_t pcrop1er; - u32_t wrp1ar; - u32_t wrp1br; - u32_t rsvd_2[4]; + volatile union __ef_acr acr; + volatile u32_t pdkeyr; + volatile u32_t keyr; + volatile u32_t optkeyr; + volatile u32_t sr; + volatile u32_t cr; + volatile u32_t eccr; + volatile u32_t rsvd_0; + volatile u32_t optr; + volatile u32_t pcrop1sr; + volatile u32_t pcrop1er; + volatile u32_t wrp1ar; + volatile u32_t wrp1br; + volatile u32_t rsvd_2[4]; /* * The registers below are only present on STM32L4x2, STM32L4x5, * STM32L4x6. */ - u32_t pcrop2sr; - u32_t pcrop2er; - u32_t wrp2ar; - u32_t wrp2br; + volatile u32_t pcrop2sr; + volatile u32_t pcrop2er; + volatile u32_t wrp2ar; + volatile u32_t wrp2br; }; #endif /* _STM32L4X_FLASH_REGISTERS_H_ */ diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index d64c304d967e7d..0488bf23db2b74 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.stm32fxx" +source "drivers/flash/Kconfig.stm32" diff --git a/drivers/flash/Kconfig.stm32fxx b/drivers/flash/Kconfig.stm32 similarity index 60% rename from drivers/flash/Kconfig.stm32fxx rename to drivers/flash/Kconfig.stm32 index a58c05cefd4c5e..931b0e5b7b08ce 100644 --- a/drivers/flash/Kconfig.stm32fxx +++ b/drivers/flash/Kconfig.stm32 @@ -1,6 +1,7 @@ -# Kconfig - ST Microelectronics STM32F3x MCUs Flash driver config +# Kconfig - ST Microelectronics STM32 MCUs Flash driver config # # Copyright (c) 2016 RnDity Sp. z o.o. +# Copyright (c) 2017 BayLibre, SAS # # SPDX-License-Identifier: Apache-2.0 # @@ -10,10 +11,10 @@ if FLASH && SOC_FAMILY_STM32 menuconfig SOC_FLASH_STM32 bool prompt "STM32 flash driver" - depends on (SOC_SERIES_STM32F3X || SOC_SERIES_STM32F4X) + depends on (SOC_SERIES_STM32F3X || SOC_SERIES_STM32F4X || SOC_SERIES_STM32L4X) default y help - Enable STM32F3x OR STM32F4x series flash driver. + Enable STM32F3x, STM32F4x OR STM32L4x 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 beb28f2c91dd92..4cfa89f14402bb 100644 --- a/drivers/flash/Makefile +++ b/drivers/flash/Makefile @@ -9,5 +9,9 @@ obj-$(CONFIG_SOC_FLASH_STM32) += flash_stm32f3x_priv.o endif ifeq ($(CONFIG_SOC_SERIES_STM32F4X),y) -obj-$(CONFIG_SOC_FLASH_STM32) += flash_stm32f4x.o +obj-$(CONFIG_SOC_FLASH_STM32) += flash_stm32.o flash_stm32f4x.o +endif + +ifeq ($(CONFIG_SOC_SERIES_STM32L4X),y) +obj-$(CONFIG_SOC_FLASH_STM32) += flash_stm32.o flash_stm32l4x.o endif diff --git a/drivers/flash/flash_stm32.c b/drivers/flash/flash_stm32.c new file mode 100644 index 00000000000000..12db619bdee0d5 --- /dev/null +++ b/drivers/flash/flash_stm32.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2017 Linaro Limited + * Copyright (c) 2017 BayLibre, SAS. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define STM32_FLASH_TIMEOUT ((u32_t) 0x000B0000) + +int flash_stm32_check_status(struct flash_stm32_priv *p) +{ +#if defined(CONFIG_SOC_SERIES_STM32F4X) + struct stm32f4x_flash *regs = p->regs; +#elif defined(CONFIG_SOC_SERIES_STM32L4X) + struct stm32l4x_flash *regs = p->regs; +#endif + + u32_t const error = + FLASH_FLAG_WRPERR | + FLASH_FLAG_PGAERR | +#if defined(FLASH_FLAG_RDERR) + FLASH_FLAG_RDERR | +#endif +#if defined(FLASH_FLAG_PGPERR) + FLASH_FLAG_PGPERR | +#endif + FLASH_FLAG_PGSERR | + FLASH_FLAG_OPERR; + + if (regs->sr & error) { + return -EIO; + } + + return 0; +} + +int flash_stm32_wait_flash_idle(struct flash_stm32_priv *p) +{ +#if defined(CONFIG_SOC_SERIES_STM32F4X) + struct stm32f4x_flash *regs = p->regs; +#elif defined(CONFIG_SOC_SERIES_STM32L4X) + struct stm32l4x_flash *regs = p->regs; +#endif + u32_t timeout = STM32_FLASH_TIMEOUT; + int rc; + + rc = flash_stm32_check_status(p); + if (rc < 0) { + return -EIO; + } + + while ((regs->sr & FLASH_SR_BSY) && timeout) { + timeout--; + } + + if (!timeout) { + return -EIO; + } + + return 0; +} + +void flash_stm32_flush_caches(struct flash_stm32_priv *p) +{ +#if defined(CONFIG_SOC_SERIES_STM32F4X) + struct stm32f4x_flash *regs = p->regs; +#elif defined(CONFIG_SOC_SERIES_STM32L4X) + struct stm32l4x_flash *regs = p->regs; +#endif + + 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_stm32_read(struct device *dev, off_t offset, void *data, + size_t len) +{ + if (!flash_stm32_valid_range(offset, len)) { + return -EINVAL; + } + + if (!len) { + return 0; + } + + memcpy(data, (void *) CONFIG_FLASH_BASE_ADDRESS + offset, len); + + return 0; +} + +int flash_stm32_erase(struct device *dev, off_t offset, size_t len) +{ + struct flash_stm32_priv *p = dev->driver_data; + int rc; + + if (!flash_stm32_valid_range(offset, len)) { + return -EINVAL; + } + + if (!len) { + return 0; + } + + k_sem_take(&p->sem, K_FOREVER); + + rc = flash_stm32_block_erase_loop(offset, len, p); + + flash_stm32_flush_caches(p); + + k_sem_give(&p->sem); + + return rc; +} + +int flash_stm32_write(struct device *dev, off_t offset, + const void *data, size_t len) +{ + struct flash_stm32_priv *p = dev->driver_data; + int rc; + + if (!flash_stm32_valid_range(offset, len)) { + return -EINVAL; + } + + if (!len) { + return 0; + } + + k_sem_take(&p->sem, K_FOREVER); + + rc = flash_stm32_write_range(offset, data, len, p); + + k_sem_give(&p->sem); + + return rc; +} + +static int flash_stm32_write_protection(struct device *dev, bool enable) +{ + struct flash_stm32_priv *p = dev->driver_data; +#if defined(CONFIG_SOC_SERIES_STM32F4X) + struct stm32f4x_flash *regs = p->regs; +#elif defined(CONFIG_SOC_SERIES_STM32L4X) + struct stm32l4x_flash *regs = p->regs; +#endif + int rc = 0; + + k_sem_take(&p->sem, K_FOREVER); + + if (enable) { + rc = flash_stm32_wait_flash_idle(p); + if (rc) { + k_sem_give(&p->sem); + return rc; + } + regs->cr |= FLASH_CR_LOCK; + } else { + if (regs->cr & FLASH_CR_LOCK) { + regs->keyr = FLASH_KEY1; + regs->keyr = FLASH_KEY2; + } + } + + k_sem_give(&p->sem); + + return rc; +} + +static struct flash_stm32_priv flash_data = { +#if defined(CONFIG_SOC_SERIES_STM32F4X) + .regs = (struct stm32f4x_flash *) FLASH_R_BASE, +#elif defined(CONFIG_SOC_SERIES_STM32L4X) + .regs = (struct stm32l4x_flash *) FLASH_R_BASE, + .pclken = { .bus = STM32_CLOCK_BUS_AHB1, + .enr = LL_AHB1_GRP1_PERIPH_FLASH }, +#endif +}; + +static const struct flash_driver_api flash_stm32_api = { + .write_protection = flash_stm32_write_protection, + .erase = flash_stm32_erase, + .write = flash_stm32_write, + .read = flash_stm32_read, +}; + +static int stm32_flash_init(struct device *dev) +{ + struct flash_stm32_priv *p = dev->driver_data; +#if defined(CONFIG_SOC_SERIES_STM32L4X) + struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME); + + /* enable clock */ + clock_control_on(clk, (clock_control_subsys_t *)&p->pclken); +#endif + + k_sem_init(&p->sem, 1, 1); + + return flash_stm32_write_protection(dev, false); +} + +DEVICE_AND_API_INIT(stm32_flash, CONFIG_SOC_FLASH_STM32_DEV_NAME, + stm32_flash_init, &flash_data, NULL, POST_KERNEL, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &flash_stm32_api); + diff --git a/drivers/flash/flash_stm32.h b/drivers/flash/flash_stm32.h new file mode 100644 index 00000000000000..2b8bf90cf39423 --- /dev/null +++ b/drivers/flash/flash_stm32.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 Linaro Limited + * Copyright (c) 2017 BayLibre, SAS. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef DRIVERS_FLASH_STM32_H +#define DRIVERS_FLASH_STM32_H + +#include + +#if defined(CONFIG_SOC_SERIES_STM32F4X) +#include +#endif + +#if defined(CONFIG_SOC_SERIES_STM32L4X) +#include +#include +#endif + +struct flash_stm32_priv { +#if defined(CONFIG_SOC_SERIES_STM32F4X) + struct stm32f4x_flash *regs; +#elif defined(CONFIG_SOC_SERIES_STM32L4X) + struct stm32l4x_flash *regs; + /* clock subsystem driving this peripheral */ + struct stm32_pclken pclken; +#endif + struct k_sem sem; +}; + +bool flash_stm32_valid_range(off_t offset, u32_t len); + +int flash_stm32_write_range(unsigned int offset, const void *data, + unsigned int len, struct flash_stm32_priv *p); + +int flash_stm32_block_erase_loop(unsigned int offset, unsigned int len, + struct flash_stm32_priv *p); + +void flash_stm32_flush_caches(struct flash_stm32_priv *p); + +int flash_stm32_wait_flash_idle(struct flash_stm32_priv *p); + +int flash_stm32_check_status(struct flash_stm32_priv *p); + +int flash_stm32_erase(struct device *dev, off_t offset, size_t len); + +int flash_stm32_write(struct device *dev, off_t offset, + const void *data, size_t len); + +#endif /* DRIVERS_FLASH_STM32_H */ diff --git a/drivers/flash/flash_stm32f4x.c b/drivers/flash/flash_stm32f4x.c index d6bc1e8855fc5c..e3b361dbbeb9dd 100644 --- a/drivers/flash/flash_stm32f4x.c +++ b/drivers/flash/flash_stm32f4x.c @@ -11,261 +11,101 @@ #include #include -#include -#include +#include -struct flash_priv { - struct stm32f4x_flash *regs; - struct k_sem sem; -}; - -static bool valid_range(off_t offset, u32_t len) +bool flash_stm32_valid_range(off_t offset, u32_t len) { return offset >= 0 && (offset + len - 1 <= STM32F4X_FLASH_END); } -static int check_status(struct stm32f4x_flash *regs) -{ - u32_t const error = - FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | -#if defined(FLASH_FLAG_RDERR) - FLASH_FLAG_RDERR | -#endif - 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) -{ - u32_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, u8_t val, struct stm32f4x_flash *regs) +static int write_byte(off_t offset, u8_t val, struct flash_stm32_priv *p) { + struct stm32f4x_flash *regs = p->regs; u32_t tmp; int rc; /* if the control register is locked, do not fail silently */ - if (regs->ctrl & FLASH_CR_LOCK) { + if (regs->cr & FLASH_CR_LOCK) { return -EIO; } - rc = wait_flash_idle(regs); + rc = flash_stm32_wait_flash_idle(p); if (rc < 0) { return rc; } - regs->ctrl &= ~CR_PSIZE_MASK; - regs->ctrl |= FLASH_PSIZE_BYTE; - regs->ctrl |= FLASH_CR_PG; + regs->cr &= ~CR_PSIZE_MASK; + regs->cr |= FLASH_PSIZE_BYTE; + regs->cr |= FLASH_CR_PG; /* flush the register write */ - tmp = regs->ctrl; + tmp = regs->cr; *((u8_t *) offset + CONFIG_FLASH_BASE_ADDRESS) = val; - rc = wait_flash_idle(regs); - regs->ctrl &= (~FLASH_CR_PG); + rc = flash_stm32_wait_flash_idle(p); + regs->cr &= (~FLASH_CR_PG); return rc; } -static int erase_sector(u16_t sector, struct stm32f4x_flash *regs) +static int erase_sector(u16_t sector, struct flash_stm32_priv *p) { + struct stm32f4x_flash *regs = p->regs; u32_t tmp; int rc; /* if the control register is locked, do not fail silently */ - if (regs->ctrl & FLASH_CR_LOCK) { + if (regs->cr & FLASH_CR_LOCK) { return -EIO; } - rc = wait_flash_idle(regs); + rc = flash_stm32_wait_flash_idle(p); if (rc < 0) { return rc; } - regs->ctrl &= STM32F4X_SECTOR_MASK; - regs->ctrl |= FLASH_CR_SER | (sector << 3); - regs->ctrl |= FLASH_CR_STRT; + regs->cr &= STM32F4X_SECTOR_MASK; + regs->cr |= FLASH_CR_SER | (sector << 3); + regs->cr |= FLASH_CR_STRT; /* flush the register write */ - tmp = regs->ctrl; + tmp = regs->cr; - rc = wait_flash_idle(regs); - regs->ctrl &= ~(FLASH_CR_SER | FLASH_CR_SNB); + rc = flash_stm32_wait_flash_idle(p); + regs->cr &= ~(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) +int flash_stm32_block_erase_loop(unsigned int offset, unsigned int len, + struct flash_stm32_priv *p) { - 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); + rc = erase_sector(i, p); 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) +int flash_stm32_write_range(unsigned int offset, const void *data, + unsigned int len, struct flash_stm32_priv *p) { - 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); + int i, rc = 0; for (i = 0; i < len; i++, offset++) { - rc = write_byte(offset, ((const u8_t *) data)[i], p->regs); + rc = write_byte(offset, ((const u8_t *) data)[i], p); 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); - diff --git a/drivers/flash/flash_stm32l4x.c b/drivers/flash/flash_stm32l4x.c new file mode 100644 index 00000000000000..48a5834f055247 --- /dev/null +++ b/drivers/flash/flash_stm32l4x.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017 Linaro Limited + * Copyright (c) 2017 BayLibre, SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define STM32L4X_BANK_SIZE_MAX 512 +#define STM32L4X_PAGE_SHIFT 11 + +#define STM32L4X_FLASH_END \ + ((u32_t)(STM32L4X_BANK_SIZE_MAX << STM32L4X_PAGE_SHIFT) - 1) + +/* offset and len must be aligned on 8, positive and not beyond end of flash */ +bool flash_stm32_valid_range(off_t offset, u32_t len) +{ + return offset % 8 == 0 && + len % 8 == 0 && + offset >= 0 && + (offset + len - 1 <= STM32L4X_FLASH_END); +} + +/* STM32L4xx devices can have up to 512 2K pages on two 256x2K pages banks */ +static unsigned int get_page(off_t offset) +{ + return offset >> STM32L4X_PAGE_SHIFT; +} + +static int write_dword(off_t offset, u64_t val, struct flash_stm32_priv *p) +{ + volatile u32_t *flash = (u32_t *)(offset + CONFIG_FLASH_BASE_ADDRESS); + struct stm32l4x_flash *regs = p->regs; + u32_t tmp; + int rc; + + /* if the control register is locked, do not fail silently */ + if (regs->cr & FLASH_CR_LOCK) { + return -EIO; + } + + /* Check that no Flash main memory operation is ongoing */ + rc = flash_stm32_wait_flash_idle(p); + if (rc < 0) { + return rc; + } + + /* Check if this double word is erased */ + if (flash[0] != 0xFFFFFFFFUL || + flash[1] != 0xFFFFFFFFUL) { + return -EIO; + } + + /* Set the PG bit */ + regs->cr |= FLASH_CR_PG; + + /* Flush the register write */ + tmp = regs->cr; + + /* Perform the data write operation at the desired memory address */ + flash[0] = (uint32_t)val; + flash[1] = (uint32_t)(val >> 32); + + /* Wait until the BSY bit is cleared */ + rc = flash_stm32_wait_flash_idle(p); + + /* Clear the PG bit */ + regs->cr &= (~FLASH_CR_PG); + + return rc; +} + +static int erase_page(unsigned int page, struct flash_stm32_priv *p) +{ + struct stm32l4x_flash *regs = p->regs; + u32_t tmp; + int rc; + + /* if the control register is locked, do not fail silently */ + if (regs->cr & FLASH_CR_LOCK) { + return -EIO; + } + + /* Check that no Flash memory operation is ongoing */ + rc = flash_stm32_wait_flash_idle(p); + if (rc < 0) { + return rc; + } + + /* Set the PER bit and select the page you wish to erase */ + regs->cr |= FLASH_CR_PER; +#ifdef FLASH_CR_BKER + regs->cr &= ~FLASH_CR_BKER_Msk; + /* Select bank, only for DUALBANK devices */ + if (page >= 256) + regs->cr |= FLASH_CR_BKER; +#endif + regs->cr &= ~FLASH_CR_PNB_Msk; + regs->cr |= ((page % 256) << 3); + + /* Set the STRT bit */ + regs->cr |= FLASH_CR_STRT; + + /* flush the register write */ + tmp = regs->cr; + + /* Wait for the BSY bit */ + rc = flash_stm32_wait_flash_idle(p); + + regs->cr &= ~FLASH_CR_PER; + + return rc; +} + +int flash_stm32_block_erase_loop(unsigned int offset, unsigned int len, + struct flash_stm32_priv *p) +{ + int i, rc = 0; + + i = get_page(offset); + for (; i <= get_page(offset + len - 1) ; ++i) { + rc = erase_page(i, p); + if (rc < 0) { + break; + } + } + + return rc; +} + +int flash_stm32_write_range(unsigned int offset, const void *data, + unsigned int len, struct flash_stm32_priv *p) +{ + int i, rc = 0; + + for (i = 0; i < len; i += 8, offset += 8) { + rc = write_dword(offset, ((const u64_t *) data)[i], p); + if (rc < 0) { + return rc; + } + } + + return rc; +}