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:
parent
b9da052e51
commit
69f0e3b15f
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
Loading…
Reference in a new issue