From d777413b196e4693d0a9e3f27ff911490f0e676d Mon Sep 17 00:00:00 2001 From: Erwan Gouriou Date: Wed, 27 Mar 2019 16:52:37 +0100 Subject: [PATCH] drivers/flash: stm32wb: Add driver for stm32wb Add flash driver for stm32wb Signed-off-by: Erwan Gouriou --- drivers/flash/CMakeLists.txt | 1 + drivers/flash/Kconfig.stm32 | 7 +- drivers/flash/flash_stm32.c | 15 +- drivers/flash/flash_stm32.h | 6 + drivers/flash/flash_stm32wbx.c | 192 ++++++++++++++++++ dts/arm/st/wb/stm32wb.dtsi | 3 + .../st,stm32wb-flash-controller.yaml | 26 +++ soc/arm/st_stm32/stm32wb/dts_fixup.h | 4 + soc/arm/st_stm32/stm32wb/flash_registers.h | 63 ++++++ 9 files changed, 314 insertions(+), 3 deletions(-) create mode 100644 drivers/flash/flash_stm32wbx.c create mode 100644 dts/bindings/flash_controller/st,stm32wb-flash-controller.yaml create mode 100644 soc/arm/st_stm32/stm32wb/flash_registers.h diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index 5b0150dbacb..8bc0c734ef1 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -24,6 +24,7 @@ if(CONFIG_CLOCK_CONTROL_STM32_CUBE) zephyr_sources_ifdef(CONFIG_SOC_SERIES_STM32F4X flash_stm32f4x.c) zephyr_sources_ifdef(CONFIG_SOC_SERIES_STM32F7X flash_stm32f7x.c) zephyr_sources_ifdef(CONFIG_SOC_SERIES_STM32L4X flash_stm32l4x.c) + zephyr_sources_ifdef(CONFIG_SOC_SERIES_STM32WBX flash_stm32wbx.c) endif() zephyr_include_directories_ifdef( diff --git a/drivers/flash/Kconfig.stm32 b/drivers/flash/Kconfig.stm32 index 6113707a3d2..7890f6c4012 100644 --- a/drivers/flash/Kconfig.stm32 +++ b/drivers/flash/Kconfig.stm32 @@ -10,7 +10,7 @@ if SOC_FAMILY_STM32 menuconfig SOC_FLASH_STM32 bool "STM32 flash driver" - depends on (SOC_SERIES_STM32F0X || SOC_SERIES_STM32F3X || SOC_SERIES_STM32F4X || SOC_SERIES_STM32F7X || SOC_SERIES_STM32L4X) + depends on (SOC_SERIES_STM32F0X || SOC_SERIES_STM32F3X || SOC_SERIES_STM32F4X || SOC_SERIES_STM32F7X || SOC_SERIES_STM32L4X || SOC_SERIES_STM32WBX) select FLASH_HAS_DRIVER_ENABLED default y select FLASH_PAGE_LAYOUT if SOC_SERIES_STM32F0X @@ -18,12 +18,15 @@ menuconfig SOC_FLASH_STM32 select FLASH_PAGE_LAYOUT if SOC_SERIES_STM32F4X select FLASH_PAGE_LAYOUT if SOC_SERIES_STM32F7X select FLASH_PAGE_LAYOUT if SOC_SERIES_STM32L4X + select FLASH_PAGE_LAYOUT if SOC_SERIES_STM32WBX select FLASH_HAS_PAGE_LAYOUT if SOC_SERIES_STM32F0X select FLASH_HAS_PAGE_LAYOUT if SOC_SERIES_STM32F3X select FLASH_HAS_PAGE_LAYOUT if SOC_SERIES_STM32F4X select FLASH_HAS_PAGE_LAYOUT if SOC_SERIES_STM32F7X select FLASH_HAS_PAGE_LAYOUT if SOC_SERIES_STM32L4X + select FLASH_HAS_PAGE_LAYOUT if SOC_SERIES_STM32WBX help - Enable STM32F0x, STM32F3x, STM32F4x, STM32F7x OR STM32L4x series flash driver. + Enable STM32F0x, STM32F3x, STM32F4x, STM32F7x, STM32L4x or + STM32WBx series flash driver. endif diff --git a/drivers/flash/flash_stm32.c b/drivers/flash/flash_stm32.c index 9bdd0ce88ca..9ec05b95512 100644 --- a/drivers/flash/flash_stm32.c +++ b/drivers/flash/flash_stm32.c @@ -29,6 +29,9 @@ /* STM32L4: maximum erase time of 24.47ms for a 2K sector */ #elif defined(CONFIG_SOC_SERIES_STM32L4X) #define STM32_FLASH_TIMEOUT (K_MSEC(25)) +/* STM32WB: maximum erase time of 24.5ms for a 4K sector */ +#elif defined(CONFIG_SOC_SERIES_STM32WBX) +#define STM32_FLASH_TIMEOUT (K_MSEC(25)) #endif /* @@ -46,6 +49,7 @@ static inline void flash_stm32_sem_give(struct device *dev) k_sem_give(&FLASH_STM32_PRIV(dev)->sem); } +#if !defined(CONFIG_SOC_SERIES_STM32WBX) static int flash_stm32_check_status(struct device *dev) { u32_t const error = @@ -75,6 +79,7 @@ static int flash_stm32_check_status(struct device *dev) return 0; } +#endif /* CONFIG_SOC_SERIES_STM32WBX */ int flash_stm32_wait_flash_idle(struct device *dev) { @@ -102,13 +107,17 @@ static void flash_stm32_flush_caches(struct device *dev, ARG_UNUSED(dev); ARG_UNUSED(offset); ARG_UNUSED(len); -#elif defined(CONFIG_SOC_SERIES_STM32F4X) || defined(CONFIG_SOC_SERIES_STM32L4X) +#elif defined(CONFIG_SOC_SERIES_STM32F4X) || \ + defined(CONFIG_SOC_SERIES_STM32L4X) || \ + defined(CONFIG_SOC_SERIES_STM32WBX) 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); +#elif defined(CONFIG_SOC_SERIES_STM32WBX) + struct stm32wbx_flash *regs = FLASH_STM32_REGS(dev); #endif if (regs->acr.val & FLASH_ACR_DCEN) { regs->acr.val &= ~FLASH_ACR_DCEN; @@ -195,6 +204,8 @@ static int flash_stm32_write_protection(struct device *dev, bool enable) struct stm32f3x_flash *regs = FLASH_STM32_REGS(dev); #elif defined(CONFIG_SOC_SERIES_STM32L4X) struct stm32l4x_flash *regs = FLASH_STM32_REGS(dev); +#elif defined(CONFIG_SOC_SERIES_STM32WBX) + struct stm32wbx_flash *regs = FLASH_STM32_REGS(dev); #endif int rc = 0; @@ -236,6 +247,8 @@ static struct flash_stm32_priv flash_data = { .regs = (struct stm32l4x_flash *) DT_FLASH_DEV_BASE_ADDRESS, .pclken = { .bus = STM32_CLOCK_BUS_AHB1, .enr = LL_AHB1_GRP1_PERIPH_FLASH }, +#elif defined(CONFIG_SOC_SERIES_STM32WBX) + .regs = (struct stm32wbx_flash *) DT_FLASH_DEV_BASE_ADDRESS, #endif }; diff --git a/drivers/flash/flash_stm32.h b/drivers/flash/flash_stm32.h index 5ad5f7c9643..4eed71929be 100644 --- a/drivers/flash/flash_stm32.h +++ b/drivers/flash/flash_stm32.h @@ -34,6 +34,8 @@ struct flash_stm32_priv { struct stm32l4x_flash *regs; /* clock subsystem driving this peripheral */ struct stm32_pclken pclken; +#elif defined(CONFIG_SOC_SERIES_STM32WBX) + struct stm32wbx_flash *regs; #endif struct k_sem sem; }; @@ -64,6 +66,10 @@ int flash_stm32_block_erase_loop(struct device *dev, unsigned int offset, int flash_stm32_wait_flash_idle(struct device *dev); +#ifdef CONFIG_SOC_SERIES_STM32WBX +int flash_stm32_check_status(struct device *dev); +#endif /* CONFIG_SOC_SERIES_STM32WBX */ + #ifdef CONFIG_FLASH_PAGE_LAYOUT void flash_stm32_page_layout(struct device *dev, const struct flash_pages_layout **layout, diff --git a/drivers/flash/flash_stm32wbx.c b/drivers/flash/flash_stm32wbx.c new file mode 100644 index 00000000000..f849db9762e --- /dev/null +++ b/drivers/flash/flash_stm32wbx.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2019 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_DOMAIN flash_stm32wb +#define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL +#include +LOG_MODULE_REGISTER(LOG_DOMAIN); + +#include +#include +#include +#include +#include +#include +#include + +#include "flash_stm32.h" + +#define STM32WBX_PAGE_SHIFT 12 + +/* offset and len must be aligned on 8 for write, + * positive and not beyond end of flash + */ +bool flash_stm32_valid_range(struct device *dev, off_t offset, u32_t len, + bool write) +{ + return (!write || (offset % 8 == 0 && len % 8 == 0U)) && + flash_stm32_range_exists(dev, offset, len); +} + +/* + * Up to 255 4K pages + */ +static u32_t get_page(off_t offset) +{ + return offset >> STM32WBX_PAGE_SHIFT; +} + +static int write_dword(struct device *dev, off_t offset, u64_t val) +{ + volatile u32_t *flash = (u32_t *)(offset + CONFIG_FLASH_BASE_ADDRESS); + struct stm32wbx_flash *regs = FLASH_STM32_REGS(dev); + u32_t tmp; + int ret, rc; + + /* if the control register is locked, do not fail silently */ + if (regs->cr & FLASH_CR_LOCK) { + rc = -EIO; + } + + /* Check if this double word is erased */ + if (flash[0] != 0xFFFFFFFFUL || + flash[1] != 0xFFFFFFFFUL) { + return -EIO; + } + + ret = flash_stm32_check_status(dev); + if (ret < 0) { + 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] = (u32_t)val; + flash[1] = (u32_t)(val >> 32); + + /* Wait until the BSY bit is cleared */ + rc = flash_stm32_wait_flash_idle(dev); + + /* Clear the PG bit */ + regs->cr &= (~FLASH_CR_PG); + + return 0; +} + +static int erase_page(struct device *dev, u32_t page) +{ + struct stm32wbx_flash *regs = FLASH_STM32_REGS(dev); + 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(dev); + if (rc < 0) { + return rc; + } + + /* Check erase operation allowed */ + if (regs->cr & FLASH_SR_PESD) { + return -EBUSY; + } + + /* Proceed to erase the page */ + regs->cr |= FLASH_CR_PER; + regs->cr &= ~FLASH_CR_PNB_Msk; + regs->cr |= page << FLASH_CR_PNB_Pos; + + regs->cr |= FLASH_CR_STRT; + + /* Wait for the BSY bit */ + rc = flash_stm32_wait_flash_idle(dev); + + regs->cr &= (~FLASH_TYPEERASE_PAGES); + + 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; + } + } + + return rc; +} + +int flash_stm32_write_range(struct device *dev, unsigned int offset, + const void *data, unsigned int len) +{ + int i, rc = 0; + + for (i = 0; i < len; i += 8, offset += 8U) { + rc = write_dword(dev, offset, + UNALIGNED_GET((const u64_t *) data + (i >> 3))); + if (rc < 0) { + return rc; + } + } + + return rc; +} + +void flash_stm32_page_layout(struct device *dev, + const struct flash_pages_layout **layout, + size_t *layout_size) +{ + static struct flash_pages_layout stm32wb_flash_layout = { + .pages_count = 0, + .pages_size = 0, + }; + + ARG_UNUSED(dev); + + if (stm32wb_flash_layout.pages_count == 0) { + stm32wb_flash_layout.pages_count = FLASH_SIZE / FLASH_PAGE_SIZE; + stm32wb_flash_layout.pages_size = FLASH_PAGE_SIZE; + } + + *layout = &stm32wb_flash_layout; + *layout_size = 1; +} + +int flash_stm32_check_status(struct device *dev) +{ + struct stm32wbx_flash *regs = FLASH_STM32_REGS(dev); + u32_t error = 0; + + /* Save Flash errors */ + error = (regs->sr & FLASH_FLAG_SR_ERROR); + error |= (regs->eccr & FLASH_FLAG_ECCC); + + /* Clear systematic Option and Enginneering bits validity error */ + if (error & FLASH_FLAG_OPTVERR) { + regs->sr |= FLASH_FLAG_SR_ERROR; + return 0; + } + + if (error) { + return -EIO; + } + + return 0; +} diff --git a/dts/arm/st/wb/stm32wb.dtsi b/dts/arm/st/wb/stm32wb.dtsi index 98cfaec5fa6..44acbafa759 100644 --- a/dts/arm/st/wb/stm32wb.dtsi +++ b/dts/arm/st/wb/stm32wb.dtsi @@ -38,6 +38,9 @@ flash0: flash@8000000 { compatible = "soc-nv-flash"; label = "FLASH_STM32"; + + write-block-size = <8>; + erase-block-size = <4096>; }; }; diff --git a/dts/bindings/flash_controller/st,stm32wb-flash-controller.yaml b/dts/bindings/flash_controller/st,stm32wb-flash-controller.yaml new file mode 100644 index 00000000000..f5e3c200ddc --- /dev/null +++ b/dts/bindings/flash_controller/st,stm32wb-flash-controller.yaml @@ -0,0 +1,26 @@ +--- +title: STM32 WB Flash Controller +version: 0.1 + +description: > + This binding gives a base representation of the STM32 wb Flash Controller + +inherits: + !include flash-controller.yaml + +properties: + compatible: + constraint: "st,stm32wb-flash-controller" + + single-bank: + type: boolean + description: dual-bank mode not enabled (page erase 4096k) + generation: define + category: optional + + dual-bank: + type: boolean + description: dual-bank mode enabled (page erase 2048k) + generation: define + category: optional +... diff --git a/soc/arm/st_stm32/stm32wb/dts_fixup.h b/soc/arm/st_stm32/stm32wb/dts_fixup.h index 660e3b8f0f2..6ae1a58b775 100644 --- a/soc/arm/st_stm32/stm32wb/dts_fixup.h +++ b/soc/arm/st_stm32/stm32wb/dts_fixup.h @@ -80,4 +80,8 @@ #define DT_UART_STM32_LPUART_1_CLOCK_BUS DT_ST_STM32_LPUART_40008000_CLOCK_BUS #define DT_UART_STM32_LPUART_1_HW_FLOW_CONTROL DT_ST_STM32_LPUART_40008000_HW_FLOW_CONTROL +#define DT_FLASH_DEV_BASE_ADDRESS DT_ST_STM32WB_FLASH_CONTROLLER_58004000_BASE_ADDRESS +#define DT_FLASH_DEV_NAME DT_ST_STM32WB_FLASH_CONTROLLER_58004000_LABEL + + /* End of SoC Level DTS fixup file */ diff --git a/soc/arm/st_stm32/stm32wb/flash_registers.h b/soc/arm/st_stm32/stm32wb/flash_registers.h new file mode 100644 index 00000000000..c27779ed2df --- /dev/null +++ b/soc/arm/st_stm32/stm32wb/flash_registers.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _STM32WBX_FLASH_REGISTERS_H_ +#define _STM32WBX_FLASH_REGISTERS_H_ + +enum { + STM32WBX_FLASH_LATENCY_0 = 0x0, + STM32WBX_FLASH_LATENCY_1 = 0x1, + STM32WBX_FLASH_LATENCY_2 = 0x2, + STM32WBX_FLASH_LATENCY_3 = 0x3, + STM32WBX_FLASH_LATENCY_4 = 0x4, +}; + +/* 3.7.1 FLASH_ACR */ +union __ef_acr { + u32_t val; + struct { + u32_t latency :3 __packed; + u32_t rsvd__3_7 :5 __packed; + u32_t prften :1 __packed; + u32_t icen :1 __packed; + u32_t dcen :1 __packed; + u32_t icrst :1 __packed; + u32_t dcrst :1 __packed; + u32_t rsvd__13_14 :2 __packed; + u32_t pes :1 __packed; + u32_t empty :1 __packed; + u32_t rsvd__17_31 :15 __packed; + } bit; +}; + +/* FLASH register map */ +struct stm32wbx_flash { + volatile union __ef_acr acr; + volatile u32_t rsvd_0; + volatile u32_t keyr; + volatile u32_t optkeyr; + volatile u32_t sr; + volatile u32_t cr; + volatile u32_t eccr; + volatile u32_t rsvd_1; + volatile u32_t optr; + volatile u32_t pcrop1asr; + volatile u32_t pcrop1aer; + volatile u32_t wrp1ar; + volatile u32_t wrp1br; + volatile u32_t pcrop1bsr; + volatile u32_t pcrop1ber; + volatile u32_t ipccbr; + volatile u32_t rsvd_2[8]; + volatile u32_t c2acr; + volatile u32_t c2sr; + volatile u32_t c2cr; + volatile u32_t rsvd_3[7]; + volatile u32_t sfr; + volatile u32_t srrvr; +}; + +#endif /* _STM32WBX_FLASH_REGISTERS_H_ */