You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
5.3 KiB
244 lines
5.3 KiB
/* |
|
* Copyright (c) 2017 BayLibre, SAS |
|
* Copyright (c) 2019 Linaro Limited |
|
* Copyright (c) 2020 Andreas Sandberg |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(flash_stm32generic, CONFIG_FLASH_LOG_LEVEL); |
|
|
|
#include <zephyr/kernel.h> |
|
#include <zephyr/device.h> |
|
#include <string.h> |
|
#include <zephyr/drivers/flash.h> |
|
#include <zephyr/init.h> |
|
#include <zephyr/sys/barrier.h> |
|
#include <soc.h> |
|
|
|
#include "flash_stm32.h" |
|
|
|
#if FLASH_STM32_WRITE_BLOCK_SIZE == 8 |
|
typedef uint64_t flash_prg_t; |
|
#elif FLASH_STM32_WRITE_BLOCK_SIZE == 4 |
|
typedef uint32_t flash_prg_t; |
|
#elif FLASH_STM32_WRITE_BLOCK_SIZE == 2 |
|
typedef uint16_t flash_prg_t; |
|
#elif FLASH_STM32_WRITE_BLOCK_SIZE == 1 |
|
typedef uint8_t flash_prg_t; |
|
#else |
|
#error Unknown write block size |
|
#endif |
|
|
|
#if defined(FLASH_CR_PER) |
|
#define FLASH_ERASED_VALUE ((flash_prg_t)-1) |
|
#elif defined(FLASH_PECR_ERASE) |
|
#define FLASH_ERASED_VALUE 0 |
|
#else |
|
#error Unknown erase value |
|
#endif |
|
|
|
static unsigned int get_page(off_t offset) |
|
{ |
|
return offset / FLASH_PAGE_SIZE; |
|
} |
|
|
|
#if defined(FLASH_CR_PER) |
|
static int is_flash_locked(FLASH_TypeDef *regs) |
|
{ |
|
return !!(regs->CR & FLASH_CR_LOCK); |
|
} |
|
|
|
static void write_enable(FLASH_TypeDef *regs) |
|
{ |
|
regs->CR |= FLASH_CR_PG; |
|
} |
|
|
|
static void write_disable(FLASH_TypeDef *regs) |
|
{ |
|
regs->CR &= (~FLASH_CR_PG); |
|
} |
|
|
|
static void erase_page_begin(FLASH_TypeDef *regs, unsigned int page) |
|
{ |
|
/* Set the PER bit and select the page you wish to erase */ |
|
regs->CR |= FLASH_CR_PER; |
|
regs->AR = FLASH_STM32_BASE_ADDRESS + page * FLASH_PAGE_SIZE; |
|
|
|
barrier_dsync_fence_full(); |
|
|
|
/* Set the STRT bit */ |
|
regs->CR |= FLASH_CR_STRT; |
|
} |
|
|
|
static void erase_page_end(FLASH_TypeDef *regs) |
|
{ |
|
regs->CR &= ~FLASH_CR_PER; |
|
} |
|
|
|
#else |
|
|
|
static int is_flash_locked(FLASH_TypeDef *regs) |
|
{ |
|
return !!(regs->PECR & FLASH_PECR_PRGLOCK); |
|
} |
|
|
|
static void write_enable(FLASH_TypeDef *regs) |
|
{ |
|
/* Only used for half-page programming on L1x */ |
|
#if !defined(CONFIG_SOC_SERIES_STM32L1X) |
|
regs->PECR |= FLASH_PECR_PROG; |
|
#endif |
|
} |
|
|
|
static void write_disable(FLASH_TypeDef *regs) |
|
{ |
|
/* Clear the PG bit */ |
|
regs->PECR &= ~FLASH_PECR_PROG; |
|
} |
|
|
|
static void erase_page_begin(FLASH_TypeDef *regs, unsigned int page) |
|
{ |
|
volatile flash_prg_t *page_base = (flash_prg_t *)( |
|
FLASH_STM32_BASE_ADDRESS + page * FLASH_PAGE_SIZE); |
|
/* Enable programming in erase mode. An erase is triggered by |
|
* writing 0 to the first word of a page. |
|
*/ |
|
regs->PECR |= FLASH_PECR_ERASE; |
|
regs->PECR |= FLASH_PECR_PROG; |
|
|
|
barrier_dsync_fence_full(); |
|
|
|
*page_base = 0; |
|
} |
|
|
|
static void erase_page_end(FLASH_TypeDef *regs) |
|
{ |
|
/* Disable programming */ |
|
regs->PECR &= ~FLASH_PECR_PROG; |
|
regs->PECR &= ~FLASH_PECR_ERASE; |
|
} |
|
#endif |
|
|
|
static int write_value(const struct device *dev, off_t offset, |
|
flash_prg_t val) |
|
{ |
|
volatile flash_prg_t *flash = (flash_prg_t *)( |
|
offset + FLASH_STM32_BASE_ADDRESS); |
|
FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); |
|
int rc; |
|
|
|
/* if the control register is locked, do not fail silently */ |
|
if (is_flash_locked(regs)) { |
|
LOG_ERR("Flash is locked"); |
|
return -EIO; |
|
} |
|
|
|
/* Check that no Flash main memory operation is ongoing */ |
|
rc = flash_stm32_wait_flash_idle(dev); |
|
if (rc < 0) { |
|
return rc; |
|
} |
|
|
|
/* Check if this half word is erased */ |
|
if (*flash != FLASH_ERASED_VALUE) { |
|
LOG_ERR("Flash location not erased"); |
|
return -EIO; |
|
} |
|
|
|
/* Enable writing */ |
|
write_enable(regs); |
|
|
|
/* Make sure the register write has taken effect */ |
|
barrier_dsync_fence_full(); |
|
|
|
/* Perform the data write operation at the desired memory address */ |
|
*flash = val; |
|
|
|
/* Wait until the BSY bit is cleared */ |
|
rc = flash_stm32_wait_flash_idle(dev); |
|
|
|
/* Disable writing */ |
|
write_disable(regs); |
|
|
|
return rc; |
|
} |
|
|
|
int flash_stm32_block_erase_loop(const struct device *dev, |
|
unsigned int offset, |
|
unsigned int len) |
|
{ |
|
FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); |
|
int i, rc = 0; |
|
|
|
/* if the control register is locked, do not fail silently */ |
|
if (is_flash_locked(regs)) { |
|
LOG_ERR("Flash is locked"); |
|
return -EIO; |
|
} |
|
|
|
/* Check that no Flash memory operation is ongoing */ |
|
rc = flash_stm32_wait_flash_idle(dev); |
|
if (rc < 0) { |
|
return rc; |
|
} |
|
|
|
for (i = get_page(offset); i <= get_page(offset + len - 1); ++i) { |
|
erase_page_begin(regs, i); |
|
barrier_dsync_fence_full(); |
|
rc = flash_stm32_wait_flash_idle(dev); |
|
erase_page_end(regs); |
|
|
|
if (rc < 0) { |
|
break; |
|
} |
|
} |
|
|
|
return rc; |
|
} |
|
|
|
int flash_stm32_write_range(const struct device *dev, unsigned int offset, |
|
const void *data, unsigned int len) |
|
{ |
|
int i, rc = 0; |
|
flash_prg_t value; |
|
|
|
for (i = 0; i < len / sizeof(flash_prg_t); i++) { |
|
memcpy(&value, |
|
(const uint8_t *)data + i * sizeof(flash_prg_t), |
|
sizeof(flash_prg_t)); |
|
rc = write_value(dev, offset + i * sizeof(flash_prg_t), value); |
|
if (rc < 0) { |
|
return rc; |
|
} |
|
} |
|
|
|
return rc; |
|
} |
|
|
|
void flash_stm32_page_layout(const struct device *dev, |
|
const struct flash_pages_layout **layout, |
|
size_t *layout_size) |
|
{ |
|
static struct flash_pages_layout flash_layout = { |
|
.pages_count = 0, |
|
.pages_size = 0, |
|
}; |
|
|
|
ARG_UNUSED(dev); |
|
|
|
if (flash_layout.pages_count == 0) { |
|
#if defined(CONFIG_SOC_SERIES_STM32F3X) |
|
flash_layout.pages_count = |
|
DT_REG_SIZE(DT_INST(0, soc_nv_flash)) / FLASH_PAGE_SIZE; |
|
#else |
|
flash_layout.pages_count = (CONFIG_FLASH_SIZE * 1024) / |
|
FLASH_PAGE_SIZE; |
|
#endif |
|
flash_layout.pages_size = FLASH_PAGE_SIZE; |
|
} |
|
|
|
*layout = &flash_layout; |
|
*layout_size = 1; |
|
}
|
|
|