drivers: flash: Add support for STM32L0X

Add support for STM32L0X using the generic STM32 backend. This is
quite a significant change since the L0 series uses a slightly
different flash controller. Refactor the generic backend to better
support different block sizes and the L0's register interface.

Signed-off-by: Andreas Sandberg <andreas@sandberg.pp.se>
This commit is contained in:
Andreas Sandberg 2020-05-11 13:39:39 +01:00 committed by Anas Nashif
parent b9da052e51
commit 69f0e3b15f
5 changed files with 176 additions and 65 deletions

View file

@ -7,12 +7,13 @@
config SOC_FLASH_STM32
bool "STM32 flash driver"
depends on SOC_FAMILY_STM32
depends on (SOC_SERIES_STM32F0X || SOC_SERIES_STM32F1X || SOC_SERIES_STM32F3X || SOC_SERIES_STM32F4X || SOC_SERIES_STM32F7X || SOC_SERIES_STM32L4X || SOC_SERIES_STM32WBX || SOC_SERIES_STM32G0X || SOC_SERIES_STM32G4X)
depends on (SOC_SERIES_STM32F0X || SOC_SERIES_STM32F1X || SOC_SERIES_STM32F3X || SOC_SERIES_STM32F4X || SOC_SERIES_STM32F7X || SOC_SERIES_STM32L0X ||SOC_SERIES_STM32L4X || SOC_SERIES_STM32WBX || SOC_SERIES_STM32G0X || SOC_SERIES_STM32G4X)
select FLASH_HAS_DRIVER_ENABLED
default y
select SOC_FLASH_STM32_V1 if SOC_SERIES_STM32F0X
select SOC_FLASH_STM32_V1 if SOC_SERIES_STM32F1X
select SOC_FLASH_STM32_V1 if SOC_SERIES_STM32F3X
select SOC_FLASH_STM32_V1 if SOC_SERIES_STM32L0X
select FLASH_PAGE_LAYOUT if SOC_SERIES_STM32G0X
select FLASH_PAGE_LAYOUT if SOC_SERIES_STM32F4X
select FLASH_PAGE_LAYOUT if SOC_SERIES_STM32F7X

View file

@ -36,6 +36,9 @@ LOG_MODULE_REGISTER(flash_stm32, CONFIG_FLASH_LOG_LEVEL);
/* STM32F7: maximum erase time of 4s for a 256K sector */
#elif defined(CONFIG_SOC_SERIES_STM32F7X)
#define STM32_FLASH_MAX_ERASE_TIME 4000
/* STM32L0: maximum erase time of 3.2ms for a 128B page */
#elif defined(CONFIG_SOC_SERIES_STM32L0X)
#define STM32_FLASH_MAX_ERASE_TIME 4
/* STM32L4: maximum erase time of 24.47ms for a 2K sector */
#elif defined(CONFIG_SOC_SERIES_STM32L4X)
#define STM32_FLASH_MAX_ERASE_TIME 25
@ -58,15 +61,14 @@ LOG_MODULE_REGISTER(flash_stm32, CONFIG_FLASH_LOG_LEVEL);
#define CFG_HW_FLASH_SEMID 2
static const struct flash_parameters flash_stm32_parameters = {
#if DT_PROP(DT_INST(0, soc_nv_flash), write_block_size)
.write_block_size = DT_PROP(DT_INST(0, soc_nv_flash), write_block_size),
.write_block_size = FLASH_STM32_WRITE_BLOCK_SIZE,
/* Some SoCs (L0/L1) use an EEPROM under the hood. Distinguish
* between them based on the presence of the PECR register. */
#if defined(FLASH_PECR_ERASE)
.erase_value = 0,
#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
/* WARNING: This value may be not valid for L0/L1 chips */
.erase_value = 0xff,
#endif
};
#if defined(CONFIG_MULTITHREADING)
@ -278,13 +280,39 @@ static int flash_stm32_write_protection(struct device *dev, bool enable)
flash_stm32_sem_give(dev);
return rc;
}
}
#if defined(FLASH_CR_LOCK)
if (enable) {
regs->CR |= FLASH_CR_LOCK;
LOG_DBG("Enable write protection");
} else {
if (regs->CR & FLASH_CR_LOCK) {
regs->KEYR = FLASH_KEY1;
regs->KEYR = FLASH_KEY2;
}
}
#else
if (enable) {
regs->PECR |= FLASH_PECR_PRGLOCK;
regs->PECR |= FLASH_PECR_PELOCK;
} else {
if (regs->PECR & FLASH_PECR_PRGLOCK) {
LOG_DBG("Disabling write protection");
regs->PEKEYR = FLASH_PEKEY1;
regs->PEKEYR = FLASH_PEKEY2;
regs->PRGKEYR = FLASH_PRGKEY1;
regs->PRGKEYR = FLASH_PRGKEY2;
}
if (FLASH->PECR & FLASH_PECR_PRGLOCK) {
LOG_ERR("Unlock failed");
rc = -EIO;
}
}
#endif
if (enable) {
LOG_DBG("Enable write protection");
} else {
LOG_DBG("Disable write protection");
}

View file

@ -32,6 +32,15 @@ struct flash_stm32_priv {
struct k_sem sem;
};
#if DT_PROP(DT_INST(0, soc_nv_flash), write_block_size)
#define FLASH_STM32_WRITE_BLOCK_SIZE \
DT_PROP(DT_INST(0, soc_nv_flash), 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
#define FLASH_STM32_PRIV(dev) ((struct flash_stm32_priv *)((dev)->driver_data))
#define FLASH_STM32_REGS(dev) (FLASH_STM32_PRIV(dev)->regs)

View file

@ -1,6 +1,7 @@
/*
* Copyright (c) 2017 BayLibre, SAS
* Copyright (c) 2019 Linaro Limited
* Copyright (c) 2020 Andreas Sandberg
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -17,30 +18,113 @@ LOG_MODULE_REGISTER(flash_stm32generic, CONFIG_FLASH_LOG_LEVEL);
#include "flash_stm32.h"
/* offset and len must be aligned on 2 for write
* positive and not beyond end of flash
*/
bool flash_stm32_valid_range(struct device *dev, off_t offset, uint32_t len,
bool write)
{
return (!write || (offset % 2 == 0 && len % 2 == 0U)) &&
flash_stm32_range_exists(dev, offset, len);
}
#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;
#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;
}
static int write_hword(struct device *dev, off_t offset, uint16_t val)
#if defined(FLASH_CR_PER)
static int is_flash_locked(FLASH_TypeDef *regs)
{
volatile uint16_t *flash = (uint16_t *)(offset + CONFIG_FLASH_BASE_ADDRESS);
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 = CONFIG_FLASH_BASE_ADDRESS + page * FLASH_PAGE_SIZE;
__DSB();
/* 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)
{
regs->PECR |= FLASH_PECR_PROG;
}
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 *)(
CONFIG_FLASH_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;
__DSB();
*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(struct device *dev, off_t offset, flash_prg_t val)
{
volatile flash_prg_t *flash = (flash_prg_t *)(
offset + CONFIG_FLASH_BASE_ADDRESS);
FLASH_TypeDef *regs = FLASH_STM32_REGS(dev);
uint32_t tmp;
int rc;
/* if the control register is locked, do not fail silently */
if (regs->CR & FLASH_CR_LOCK) {
if (is_flash_locked(regs)) {
LOG_ERR("Flash is locked");
return -EIO;
}
@ -51,15 +135,16 @@ static int write_hword(struct device *dev, off_t offset, uint16_t val)
}
/* Check if this half word is erased */
if (*flash != 0xFFFF) {
if (*flash != FLASH_ERASED_VALUE) {
LOG_DBG("Flash location not erased");
return -EIO;
}
/* Set the PG bit */
regs->CR |= FLASH_CR_PG;
/* Enable writing */
write_enable(regs);
/* Flush the register write */
tmp = regs->CR;
/* Make sure the register write has taken effect */
__DSB();
/* Perform the data write operation at the desired memory address */
*flash = val;
@ -67,21 +152,31 @@ static int write_hword(struct device *dev, off_t offset, uint16_t val)
/* Wait until the BSY bit is cleared */
rc = flash_stm32_wait_flash_idle(dev);
/* Clear the PG bit */
regs->CR &= (~FLASH_CR_PG);
/* Disable writing */
write_disable(regs);
return rc;
}
static int erase_page(struct device *dev, unsigned int page)
/* offset and len must be aligned on 2 for write
* positive and not beyond end of flash
*/
bool flash_stm32_valid_range(struct device *dev, off_t offset, uint32_t len,
bool write)
{
return (!write || (offset % 2 == 0 && len % 2 == 0U)) &&
flash_stm32_range_exists(dev, offset, len);
}
int flash_stm32_block_erase_loop(struct device *dev, unsigned int offset,
unsigned int len)
{
FLASH_TypeDef *regs = FLASH_STM32_REGS(dev);
uint32_t page_address = CONFIG_FLASH_BASE_ADDRESS;
uint32_t tmp;
int rc;
int i, rc = 0;
/* if the control register is locked, do not fail silently */
if (regs->CR & FLASH_CR_LOCK) {
if (is_flash_locked(regs)) {
LOG_ERR("Flash is locked");
return -EIO;
}
@ -91,35 +186,12 @@ static int erase_page(struct device *dev, unsigned int page)
return rc;
}
/* Calculate the flash page address */
page_address += page * FLASH_PAGE_SIZE;
for (i = get_page(offset); i <= get_page(offset + len - 1); ++i) {
erase_page_begin(regs, i);
__DSB();
rc = flash_stm32_wait_flash_idle(dev);
erase_page_end(regs);
/* Set the PER bit and select the page you wish to erase */
regs->CR |= FLASH_CR_PER;
regs->AR = page_address;
/* 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(dev);
regs->CR &= ~FLASH_CR_PER;
return rc;
}
int flash_stm32_block_erase_loop(struct device *dev, unsigned int offset,
unsigned int len)
{
int i, rc = 0;
i = get_page(offset);
for (; i <= get_page(offset + len - 1) ; ++i) {
rc = erase_page(dev, i);
if (rc < 0) {
break;
}
@ -132,9 +204,11 @@ int flash_stm32_write_range(struct device *dev, unsigned int offset,
const void *data, unsigned int len)
{
int i, rc = 0;
const flash_prg_t *values = (const flash_prg_t *)data;
for (i = 0; i < len; i += 2, offset += 2U) {
rc = write_hword(dev, offset, ((const uint16_t *) data)[i>>1]);
for (i = 0; i < len / sizeof(flash_prg_t); i++) {
rc = write_value(dev, offset + i * sizeof(flash_prg_t),
values[i]);
if (rc < 0) {
return rc;
}

View file

@ -42,7 +42,6 @@
label = "RTC_0";
};
/* Driver does not currently support the l0 flash controller */
flash: flash-controller@40022000 {
compatible = "st,stm32-flash-controller", "st,stm32l0-flash-controller";
label = "FLASH_CTRL";