mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-10 06:41:56 +00:00
A common driver is shared between stm32 chips, which was not the case for stm32f3 series. As a consequence stm32f3 was not maintained equally and was missing features such as flash layout or dts based configuration. Besides, drivers had some flows such as wrong bus clock and missing HSI clock activation which lead to issues on boards not using HSI as main clock. As a consequence this commit moves stm32f3 series flash driver to common stm32 flash drivers. Fixes #4197 Signed-off-by: Erwan Gouriou <erwan.gouriou@linaro.org>
297 lines
7.3 KiB
C
297 lines
7.3 KiB
C
/*
|
|
* Copyright (c) 2017 Linaro Limited
|
|
* Copyright (c) 2017 BayLibre, SAS.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <kernel.h>
|
|
#include <device.h>
|
|
#include <string.h>
|
|
#include <flash.h>
|
|
#include <init.h>
|
|
#include <soc.h>
|
|
|
|
#include "flash_stm32.h"
|
|
|
|
/* STM32F0: maximum erase time of 40ms for a 2K sector */
|
|
#if defined(CONFIG_SOC_SERIES_STM32F0X)
|
|
#define STM32_FLASH_TIMEOUT (K_MSEC(40))
|
|
/* STM32F3: maximum erase time of 40ms for a 2K sector */
|
|
#elif defined(CONFIG_SOC_SERIES_STM32F3X)
|
|
#define STM32_FLASH_TIMEOUT (K_MSEC(40))
|
|
/* STM32F4: maximum erase time of 4s for a 128K sector */
|
|
#elif defined(CONFIG_SOC_SERIES_STM32F4X)
|
|
#define STM32_FLASH_TIMEOUT (K_MSEC(4000))
|
|
/* STM32F7: maximum erase time of 4s for a 256K sector */
|
|
#elif defined(CONFIG_SOC_SERIES_STM32F7X)
|
|
#define STM32_FLASH_TIMEOUT (K_MSEC(4000))
|
|
/* STM32L4: maximum erase time of 24.47ms for a 2K sector */
|
|
#elif defined(CONFIG_SOC_SERIES_STM32L4X)
|
|
#define STM32_FLASH_TIMEOUT (K_MSEC(25))
|
|
#endif
|
|
|
|
/*
|
|
* This is named flash_stm32_sem_take instead of flash_stm32_lock (and
|
|
* similarly for flash_stm32_sem_give) to avoid confusion with locking
|
|
* actual flash pages.
|
|
*/
|
|
static inline void flash_stm32_sem_take(struct device *dev)
|
|
{
|
|
k_sem_take(&FLASH_STM32_PRIV(dev)->sem, K_FOREVER);
|
|
}
|
|
|
|
static inline void flash_stm32_sem_give(struct device *dev)
|
|
{
|
|
k_sem_give(&FLASH_STM32_PRIV(dev)->sem);
|
|
}
|
|
|
|
static int flash_stm32_check_status(struct device *dev)
|
|
{
|
|
u32_t const error =
|
|
#if defined(FLASH_FLAG_PGAERR)
|
|
FLASH_FLAG_PGAERR |
|
|
#endif
|
|
#if defined(FLASH_FLAG_RDERR)
|
|
FLASH_FLAG_RDERR |
|
|
#endif
|
|
#if defined(FLASH_FLAG_PGPERR)
|
|
FLASH_FLAG_PGPERR |
|
|
#endif
|
|
#if defined(FLASH_FLAG_PGSERR)
|
|
FLASH_FLAG_PGSERR |
|
|
#endif
|
|
#if defined(FLASH_FLAG_OPERR)
|
|
FLASH_FLAG_OPERR |
|
|
#endif
|
|
#if defined(FLASH_FLAG_PGERR)
|
|
FLASH_FLAG_PGERR |
|
|
#endif
|
|
FLASH_FLAG_WRPERR;
|
|
|
|
if (FLASH_STM32_REGS(dev)->sr & error) {
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int flash_stm32_wait_flash_idle(struct device *dev)
|
|
{
|
|
s64_t timeout_time = k_uptime_get() + STM32_FLASH_TIMEOUT;
|
|
int rc;
|
|
|
|
rc = flash_stm32_check_status(dev);
|
|
if (rc < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
while ((FLASH_STM32_REGS(dev)->sr & FLASH_SR_BSY)) {
|
|
if (k_uptime_get() > timeout_time) {
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void flash_stm32_flush_caches(struct device *dev,
|
|
off_t offset, size_t len)
|
|
{
|
|
#if defined(CONFIG_SOC_SERIES_STM32F0X) || defined(CONFIG_SOC_SERIES_STM32F3X)
|
|
ARG_UNUSED(dev);
|
|
ARG_UNUSED(offset);
|
|
ARG_UNUSED(len);
|
|
#elif defined(CONFIG_SOC_SERIES_STM32F4X) || defined(CONFIG_SOC_SERIES_STM32L4X)
|
|
ARG_UNUSED(offset);
|
|
ARG_UNUSED(len);
|
|
#if defined(CONFIG_SOC_SERIES_STM32F4X)
|
|
struct stm32f4x_flash *regs = FLASH_STM32_REGS(dev);
|
|
#elif defined(CONFIG_SOC_SERIES_STM32L4X)
|
|
struct stm32l4x_flash *regs = FLASH_STM32_REGS(dev);
|
|
#endif
|
|
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;
|
|
}
|
|
#elif defined(CONFIG_SOC_SERIES_STM32F7X)
|
|
SCB_InvalidateDCache_by_Addr((uint32_t *)(CONFIG_FLASH_BASE_ADDRESS
|
|
+ offset), len);
|
|
#endif
|
|
}
|
|
|
|
static int flash_stm32_read(struct device *dev, off_t offset, void *data,
|
|
size_t len)
|
|
{
|
|
if (!flash_stm32_valid_range(dev, offset, len, false)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!len) {
|
|
return 0;
|
|
}
|
|
|
|
memcpy(data, (u8_t *) CONFIG_FLASH_BASE_ADDRESS + offset, len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int flash_stm32_erase(struct device *dev, off_t offset, size_t len)
|
|
{
|
|
int rc;
|
|
|
|
if (!flash_stm32_valid_range(dev, offset, len, true)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!len) {
|
|
return 0;
|
|
}
|
|
|
|
flash_stm32_sem_take(dev);
|
|
|
|
rc = flash_stm32_block_erase_loop(dev, offset, len);
|
|
|
|
flash_stm32_flush_caches(dev, offset, len);
|
|
|
|
flash_stm32_sem_give(dev);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int flash_stm32_write(struct device *dev, off_t offset,
|
|
const void *data, size_t len)
|
|
{
|
|
int rc;
|
|
|
|
if (!flash_stm32_valid_range(dev, offset, len, true)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!len) {
|
|
return 0;
|
|
}
|
|
|
|
flash_stm32_sem_take(dev);
|
|
|
|
rc = flash_stm32_write_range(dev, offset, data, len);
|
|
|
|
flash_stm32_sem_give(dev);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int flash_stm32_write_protection(struct device *dev, bool enable)
|
|
{
|
|
#if defined(CONFIG_SOC_SERIES_STM32F4X)
|
|
struct stm32f4x_flash *regs = FLASH_STM32_REGS(dev);
|
|
#elif defined(CONFIG_SOC_SERIES_STM32F7X)
|
|
struct stm32f7x_flash *regs = FLASH_STM32_REGS(dev);
|
|
#elif defined(CONFIG_SOC_SERIES_STM32F0X)
|
|
struct stm32f0x_flash *regs = FLASH_STM32_REGS(dev);
|
|
#elif defined(CONFIG_SOC_SERIES_STM32F3X)
|
|
struct stm32f3x_flash *regs = FLASH_STM32_REGS(dev);
|
|
#elif defined(CONFIG_SOC_SERIES_STM32L4X)
|
|
struct stm32l4x_flash *regs = FLASH_STM32_REGS(dev);
|
|
#endif
|
|
int rc = 0;
|
|
|
|
flash_stm32_sem_take(dev);
|
|
|
|
if (enable) {
|
|
rc = flash_stm32_wait_flash_idle(dev);
|
|
if (rc) {
|
|
flash_stm32_sem_give(dev);
|
|
return rc;
|
|
}
|
|
regs->cr |= FLASH_CR_LOCK;
|
|
} else {
|
|
if (regs->cr & FLASH_CR_LOCK) {
|
|
regs->keyr = FLASH_KEY1;
|
|
#ifdef CONFIG_SOC_SERIES_STM32F3X
|
|
/* On STM32F3 series some time is requested */
|
|
/* before writing again on key register */
|
|
flash_stm32_wait_flash_idle(dev);
|
|
#endif /* CONFIG_SOC_SERIES_STM32F3X */
|
|
regs->keyr = FLASH_KEY2;
|
|
}
|
|
}
|
|
|
|
flash_stm32_sem_give(dev);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static struct flash_stm32_priv flash_data = {
|
|
#if defined(CONFIG_SOC_SERIES_STM32F0X)
|
|
.regs = (struct stm32f0x_flash *) DT_FLASH_DEV_BASE_ADDRESS,
|
|
.pclken = { .bus = STM32_CLOCK_BUS_AHB1,
|
|
.enr = LL_AHB1_GRP1_PERIPH_FLASH },
|
|
#elif defined(CONFIG_SOC_SERIES_STM32F3X)
|
|
.regs = (struct stm32f3x_flash *) DT_FLASH_DEV_BASE_ADDRESS,
|
|
.pclken = { .bus = STM32_CLOCK_BUS_AHB1,
|
|
.enr = LL_AHB1_GRP1_PERIPH_FLASH },
|
|
#elif defined(CONFIG_SOC_SERIES_STM32F4X)
|
|
.regs = (struct stm32f4x_flash *) DT_FLASH_DEV_BASE_ADDRESS,
|
|
#elif defined(CONFIG_SOC_SERIES_STM32F7X)
|
|
.regs = (struct stm32f7x_flash *) DT_FLASH_DEV_BASE_ADDRESS,
|
|
#elif defined(CONFIG_SOC_SERIES_STM32L4X)
|
|
.regs = (struct stm32l4x_flash *) DT_FLASH_DEV_BASE_ADDRESS,
|
|
.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,
|
|
#ifdef CONFIG_FLASH_PAGE_LAYOUT
|
|
.page_layout = flash_stm32_page_layout,
|
|
#endif
|
|
#ifdef DT_SOC_NV_FLASH_0_WRITE_BLOCK_SIZE
|
|
.write_block_size = DT_SOC_NV_FLASH_0_WRITE_BLOCK_SIZE,
|
|
#else
|
|
#error Flash write block size not available
|
|
/* Flash Write block size is extracted from device tree */
|
|
/* as flash node property 'write-block-size' */
|
|
#endif
|
|
};
|
|
|
|
static int stm32_flash_init(struct device *dev)
|
|
{
|
|
struct flash_stm32_priv *p = FLASH_STM32_PRIV(dev);
|
|
#if defined(CONFIG_SOC_SERIES_STM32L4X) || \
|
|
defined(CONFIG_SOC_SERIES_STM32F0X) || \
|
|
defined(CONFIG_SOC_SERIES_STM32F3X)
|
|
struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);
|
|
|
|
/*
|
|
* On STM32F0, Flash interface clock source is always HSI,
|
|
* so statically enable HSI here.
|
|
*/
|
|
#if defined(CONFIG_SOC_SERIES_STM32F0X) || defined(CONFIG_SOC_SERIES_STM32F3X)
|
|
LL_RCC_HSI_Enable();
|
|
|
|
while (!LL_RCC_HSI_IsReady()) {
|
|
}
|
|
#endif
|
|
|
|
/* enable clock */
|
|
if (clock_control_on(clk, (clock_control_subsys_t *)&p->pclken) != 0) {
|
|
return -EIO;
|
|
}
|
|
#endif
|
|
|
|
k_sem_init(&p->sem, 1, 1);
|
|
|
|
return flash_stm32_write_protection(dev, false);
|
|
}
|
|
|
|
DEVICE_AND_API_INIT(stm32_flash, DT_FLASH_DEV_NAME,
|
|
stm32_flash_init, &flash_data, NULL, POST_KERNEL,
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &flash_stm32_api);
|