From c74a51f789ccb07d6868bafe54e55b5c6a57499e Mon Sep 17 00:00:00 2001 From: Juha Niskanen Date: Thu, 11 May 2017 14:35:27 +0300 Subject: [PATCH] STM32L4: add internal flash write support --- arch/arm/src/stm32l4/Make.defs | 2 +- arch/arm/src/stm32l4/stm32l4_flash.c | 357 +++++++++++++++++++++++++++ arch/arm/src/stm32l4/stm32l4_flash.h | 66 +++-- 3 files changed, 401 insertions(+), 24 deletions(-) create mode 100644 arch/arm/src/stm32l4/stm32l4_flash.c diff --git a/arch/arm/src/stm32l4/Make.defs b/arch/arm/src/stm32l4/Make.defs index 417d7c170d..313e4195e7 100644 --- a/arch/arm/src/stm32l4/Make.defs +++ b/arch/arm/src/stm32l4/Make.defs @@ -103,7 +103,7 @@ CHIP_CSRCS = stm32l4_allocateheap.c stm32l4_exti_gpio.c stm32l4_gpio.c CHIP_CSRCS += stm32l4_idle.c stm32l4_irq.c stm32l4_lowputc.c stm32l4_rcc.c CHIP_CSRCS += stm32l4_serial.c stm32l4_start.c stm32l4_waste.c stm32l4_uid.c CHIP_CSRCS += stm32l4_spi.c stm32l4_i2c.c stm32l4_lse.c stm32l4_pwr.c -CHIP_CSRCS += stm32l4_tim.c +CHIP_CSRCS += stm32l4_tim.c stm32l4_flash.c ifeq ($(CONFIG_TIMER),y) CHIP_CSRCS += stm32l4_tim_lowerhalf.c diff --git a/arch/arm/src/stm32l4/stm32l4_flash.c b/arch/arm/src/stm32l4/stm32l4_flash.c new file mode 100644 index 0000000000..14ac3614d6 --- /dev/null +++ b/arch/arm/src/stm32l4/stm32l4_flash.c @@ -0,0 +1,357 @@ +/************************************************************************************ + * arch/arm/src/stm32l4/stm32l4_flash.c + * + * Copyright (C) 2011 Uros Platise. All rights reserved. + * Copyright (C) 2017 Haltian Ltd. All rights reserved. + * Authors: Uros Platise + * Juha Niskanen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +/* Provides standard flash access functions, to be used by the flash mtd driver. + * The interface is defined in the include/nuttx/progmem.h + * + * Notes about this implementation: + * - HSI16 is automatically turned ON by MCU, if not enabled beforehand + * - Only Standard Programming is supported, no Fast Programming. + * - Low Power Modes are not permitted during write/erase + */ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include "stm32l4_rcc.h" +#include "stm32l4_waste.h" +#include "stm32l4_flash.h" + +#include "up_arch.h" + + +#if !(defined(CONFIG_STM32L4_STM32L4X3) || defined(CONFIG_STM32L4_STM32L4X6)) +# error "Unrecognized STM32 chip" +#endif + +#if !defined(CONFIG_STM32L4_FLASH_OVERRIDE_DEFAULT) +# warning "Flash Configuration has been overridden - make sure it is correct" +#endif + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#define FLASH_KEY1 0x45670123 +#define FLASH_KEY2 0xCDEF89AB + +#define OPTBYTES_KEY1 0x08192A3B +#define OPTBYTES_KEY2 0x4C5D6E7F + +#define FLASH_CR_PAGE_ERASE FLASH_CR_PER +#define FLASH_SR_WRITE_PROTECTION_ERROR FLASH_SR_WRPERR + +/* All errors for Standard Programming, not for other operations. */ + +#define FLASH_SR_ALLERRS (FLASH_SR_PGSERR | FLASH_SR_SIZERR | \ + FLASH_SR_PGAERR | FLASH_SR_WRPERR | \ + FLASH_SR_PROGERR) + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +static sem_t g_sem = SEM_INITIALIZER(1); + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +static inline void sem_lock(void) +{ + while (sem_wait(&g_sem) < 0) + { + DEBUGASSERT(errno == EINTR); + } +} + +static inline void sem_unlock(void) +{ + sem_post(&g_sem); +} + +static void flash_unlock(void) +{ + while (getreg32(STM32L4_FLASH_SR) & FLASH_SR_BSY) + { + up_waste(); + } + + if (getreg32(STM32L4_FLASH_CR) & FLASH_CR_LOCK) + { + /* Unlock sequence */ + + putreg32(FLASH_KEY1, STM32L4_FLASH_KEYR); + putreg32(FLASH_KEY2, STM32L4_FLASH_KEYR); + } +} + +static void flash_lock(void) +{ + modifyreg32(STM32L4_FLASH_CR, 0, FLASH_CR_LOCK); +} + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +void stm32l4_flash_unlock(void) +{ + sem_lock(); + flash_unlock(); + sem_unlock(); +} + +void stm32l4_flash_lock(void) +{ + sem_lock(); + flash_lock(); + sem_unlock(); +} + +size_t up_progmem_pagesize(size_t page) +{ + return STM32L4_FLASH_PAGESIZE; +} + +ssize_t up_progmem_getpage(size_t addr) +{ + if (addr >= STM32L4_FLASH_BASE) + { + addr -= STM32L4_FLASH_BASE; + } + + if (addr >= STM32L4_FLASH_SIZE) + { + return -EFAULT; + } + + return addr / STM32L4_FLASH_PAGESIZE; +} + +size_t up_progmem_getaddress(size_t page) +{ + if (page >= STM32L4_FLASH_NPAGES) + { + return SIZE_MAX; + } + + return page * STM32L4_FLASH_PAGESIZE + STM32L4_FLASH_BASE; +} + +size_t up_progmem_npages(void) +{ + return STM32L4_FLASH_NPAGES; +} + +bool up_progmem_isuniform(void) +{ + return true; +} + +ssize_t up_progmem_erasepage(size_t page) +{ + if (page >= STM32L4_FLASH_NPAGES) + { + return -EFAULT; + } + + sem_lock(); + + /* Get flash ready and begin erasing single page. */ + + flash_unlock(); + + modifyreg32(STM32L4_FLASH_CR, 0, FLASH_CR_PAGE_ERASE); + modifyreg32(STM32L4_FLASH_CR, FLASH_CR_PNB_MASK, FLASH_CR_PNB(page)); + modifyreg32(STM32L4_FLASH_CR, 0, FLASH_CR_START); + + while (getreg32(STM32L4_FLASH_SR) & FLASH_SR_BSY) + { + up_waste(); + } + + modifyreg32(STM32L4_FLASH_CR, FLASH_CR_PAGE_ERASE, 0); + + flash_lock(); + sem_unlock(); + + /* Verify */ + + if (up_progmem_ispageerased(page) == 0) + { + return up_progmem_pagesize(page); + } + else + { + return -EIO; + } +} + +ssize_t up_progmem_ispageerased(size_t page) +{ + size_t addr; + size_t count; + size_t bwritten = 0; + + if (page >= STM32L4_FLASH_NPAGES) + { + return -EFAULT; + } + + /* Verify */ + + for (addr = up_progmem_getaddress(page), count = up_progmem_pagesize(page); + count; count--, addr++) + { + if (getreg8(addr) != 0xff) + { + bwritten++; + } + } + + return bwritten; +} + +ssize_t up_progmem_write(size_t addr, const void *buf, size_t count) +{ + uint32_t *word = (uint32_t *)buf; + size_t written = count; + int ret = OK; + + /* STM32L4 requires double-word access and alignment. */ + + if (addr & 7) + { + return -EINVAL; + } + + /* But we can complete single-word writes by writing the + * erase value 0xffffffff as second word ourselves, so + * allow odd number of words here. + */ + + if (count & 3) + { + return -EINVAL; + } + + /* Check for valid address range. */ + + if (addr >= STM32L4_FLASH_BASE) + { + addr -= STM32L4_FLASH_BASE; + } + + if ((addr + count) > STM32L4_FLASH_SIZE) + { + return -EFAULT; + } + + sem_lock(); + + /* Get flash ready and begin flashing. */ + + flash_unlock(); + + modifyreg32(STM32L4_FLASH_CR, 0, FLASH_CR_PG); + + for (addr += STM32L4_FLASH_BASE; count; count -= 8, word += 2, addr += 8) + { + uint32_t second_word; + + /* Write first word. */ + + putreg32(*word, addr); + + /* Write second word and wait to complete. */ + + second_word = (count == 4) ? 0xffffffff : *(word + 1); + putreg32(second_word, (addr + 4)); + + while (getreg32(STM32L4_FLASH_SR) & FLASH_SR_BSY) + { + up_waste(); + } + + /* Verify */ + + if (getreg32(STM32L4_FLASH_SR) & FLASH_SR_WRITE_PROTECTION_ERROR) + { + ret = -EROFS; + goto out; + } + + if (getreg32(addr) != *word || getreg32((addr + 4)) != second_word) + { + ret = -EIO; + goto out; + } + + if (count == 4) + { + break; + } + } + +out: + modifyreg32(STM32L4_FLASH_CR, FLASH_CR_PG, 0); + + /* If there was an error, clear all error flags in status + * register (rc_w1 register so do this by writing the + * error bits). + */ + + if (ret != OK) + { + ferr("flash write error: %d, status: 0x%x\n", ret, getreg32(STM32L4_FLASH_SR)); + modifyreg32(STM32L4_FLASH_SR, 0, FLASH_SR_ALLERRS); + } + + flash_lock(); + sem_unlock(); + return (ret == OK) ? written : ret; +} diff --git a/arch/arm/src/stm32l4/stm32l4_flash.h b/arch/arm/src/stm32l4/stm32l4_flash.h index c29a8d35a5..15745b9377 100644 --- a/arch/arm/src/stm32l4/stm32l4_flash.h +++ b/arch/arm/src/stm32l4/stm32l4_flash.h @@ -1,9 +1,10 @@ /************************************************************************************ - * arch/arm/src/stm32l4/chip/stm32l4_flash.h + * arch/arm/src/stm32l4/stm32l4_flash.h * - * Copyright (C) 2009, 2011, 2015 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt - * David Sidrane + * Copyright (C) 2009, 2011, 2015, 2017 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * David Sidrane + * Juha Niskanen * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,8 +35,8 @@ * ************************************************************************************/ -#ifndef __ARCH_ARM_SRC_STM32L4_CHIP_STM32L4_FLASH_H -#define __ARCH_ARM_SRC_STM32L4_CHIP_STM32L4_FLASH_H +#ifndef __ARCH_ARM_SRC_STM32L4_STM32L4_FLASH_H +#define __ARCH_ARM_SRC_STM32L4_STM32L4_FLASH_H /************************************************************************************ * Pre-processor Definitions @@ -122,10 +123,12 @@ #define STM32L4_FLASH_PCROP1ER_OFFSET 0x0028 #define STM32L4_FLASH_WRP1AR_OFFSET 0x002c #define STM32L4_FLASH_WRP1BR_OFFSET 0x0030 -#define STM32L4_FLASH_PCROP2SR_OFFSET 0x0044 -#define STM32L4_FLASH_PCROP2ER_OFFSET 0x0048 -#define STM32L4_FLASH_WRP2AR_OFFSET 0x004c -#define STM32L4_FLASH_WRP2BR_OFFSET 0x0050 +#if defined(CONFIG_STM32L4_STM32L4X6) +# define STM32L4_FLASH_PCROP2SR_OFFSET 0x0044 +# define STM32L4_FLASH_PCROP2ER_OFFSET 0x0048 +# define STM32L4_FLASH_WRP2AR_OFFSET 0x004c +# define STM32L4_FLASH_WRP2BR_OFFSET 0x0050 +#endif /* Register Addresses ***************************************************************/ @@ -141,10 +144,12 @@ #define STM32L4_FLASH_PCROP1ER (STM32L4_FLASHIF_BASE+STM32L4_FLASH_PCROP1ER_OFFSET) #define STM32L4_FLASH_WRP1AR (STM32L4_FLASHIF_BASE+STM32L4_FLASH_WRP1AR_OFFSET) #define STM32L4_FLASH_WRP1BR (STM32L4_FLASHIF_BASE+STM32L4_FLASH_WRP1BR_OFFSET) -#define STM32L4_FLASH_PCROP2SR (STM32L4_FLASHIF_BASE+STM32L4_FLASH_PCROP2SR_OFFSET) -#define STM32L4_FLASH_PCROP2ER (STM32L4_FLASHIF_BASE+STM32L4_FLASH_PCROP2ER_OFFSET) -#define STM32L4_FLASH_WRP2AR (STM32L4_FLASHIF_BASE+STM32L4_FLASH_WRP2AR_OFFSET) -#define STM32L4_FLASH_WRP2BR (STM32L4_FLASHIF_BASE+STM32L4_FLASH_WRP2BR_OFFSET) +#if defined(CONFIG_STM32L4_STM32L4X6) +# define STM32L4_FLASH_PCROP2SR (STM32L4_FLASHIF_BASE+STM32L4_FLASH_PCROP2SR_OFFSET) +# define STM32L4_FLASH_PCROP2ER (STM32L4_FLASHIF_BASE+STM32L4_FLASH_PCROP2ER_OFFSET) +# define STM32L4_FLASH_WRP2AR (STM32L4_FLASHIF_BASE+STM32L4_FLASH_WRP2AR_OFFSET) +# define STM32L4_FLASH_WRP2BR (STM32L4_FLASHIF_BASE+STM32L4_FLASH_WRP2BR_OFFSET) +#endif /* Register Bitfield Definitions ****************************************************/ /* Flash Access Control Register (ACR) */ @@ -170,6 +175,7 @@ #define FLASH_SR_EOP (1 << 0) /* Bit 0: End of operation */ #define FLASH_SR_OPERR (1 << 1) /* Bit 1: Operation error */ +#define FLASH_SR_PROGERR (1 << 3) /* Bit 3: Programming error */ #define FLASH_SR_WRPERR (1 << 4) /* Bit 4: Write protection error */ #define FLASH_SR_PGAERR (1 << 5) /* Bit 5: Programming alignment error */ #define FLASH_SR_SIZERR (1 << 6) /* Bit 6: Size error */ @@ -179,6 +185,9 @@ #define FLASH_SR_RDERR (1 << 14) /* Bit 14: PCROP read error */ #define FLASH_SR_OPTVERR (1 << 15) /* Bit 15: Option validity error */ #define FLASH_SR_BSY (1 << 16) /* Bit 16: Busy */ +#if defined(CONFIG_STM32L4_STM32L4X3) +# define FLASH_SR_PEMPTY (1 << 17) /* Bit 17: Program empty */ +#endif /* Flash Control Register (CR) */ @@ -190,8 +199,10 @@ #define FLASH_CR_PNB_MASK (0xFF << FLASH_CR_PNB_SHIFT) #define FLASH_CR_PNB(n) ((n) << FLASH_CR_PNB_SHIFT) /* Page n (if BKER=0) or n+256 (if BKER=1), n=0..255 */ -#define FLASH_CR_BKER (1 << 11) /* Bit 11: Page number MSB (Bank selection) */ -#define FLASH_CR_MER2 (1 << 15) /* Bit 15: Mass Erase Bank 2 */ +#if defined(CONFIG_STM32L4_STM32L4X6) +# define FLASH_CR_BKER (1 << 11) /* Bit 11: Page number MSB (Bank selection) */ +# define FLASH_CR_MER2 (1 << 15) /* Bit 15: Mass Erase Bank 2 */ +#endif #define FLASH_CR_START (1 << 16) /* Bit 16: Start Erase */ #define FLASH_CR_OPTSTRT (1 << 17) /* Bit 17: Options modification Start */ #define FLASH_CR_FSTPG (1 << 23) /* Bit 23: Fast programming */ @@ -206,7 +217,9 @@ #define FLASH_ECCR_ADDR_ECC_SHIFT (0) /* Bits 8-15: Read protect */ #define FLASH_ECCR_ADDR_ECC_MASK (0x07ffff << FLASH_ECCR_ADDR_ECC_SHIFT) -#define FLASH_ECCR_BK_ECC (1 << 19) /* Bit 19: ECC fail bank */ +#if defined(CONFIG_STM32L4_STM32L4X6) +# define FLASH_ECCR_BK_ECC (1 << 19) /* Bit 19: ECC fail bank */ +#endif #define FLASH_ECCR_SYSF_ECC (1 << 20) /* Bit 20: System Flash ECC fail */ #define FLASH_ECCR_ECCCIE (1 << 24) /* Bit 24: ECC correction interrupt enable */ #define FLASH_ECCR_ECCC (1 << 30) /* Bit 30: ECC correction */ @@ -221,11 +234,18 @@ #define FLASH_OPTCR_IWDG_STOP (1 << 17) /* Bit 17: Independent watchdog counter freeze in Stop mode */ #define FLASH_OPTCR_IWDG_STDBY (1 << 18) /* Bit 18: Independent watchdog counter freeze in Standby mode*/ #define FLASH_OPTCR_WWDG_SW (1 << 19) /* Bit 19: Window watchdog selection */ -#define FLASH_OPTCR_BFB2 (1 << 20) /* Bit 20: Dual bank boot */ -#define FLASH_OPTCR_DUALBANK (1 << 21) /* Bit 21: Dual bank enable */ +#if defined(CONFIG_STM32L4_STM32L4X6) +# define FLASH_OPTCR_BFB2 (1 << 20) /* Bit 20: Dual bank boot */ +# define FLASH_OPTCR_DUALBANK (1 << 21) /* Bit 21: Dual bank enable */ +#endif #define FLASH_OPTCR_NBOOT1 (1 << 23) /* Bit 23: Boot configuration */ #define FLASH_OPTCR_SRAM2_PE (1 << 24) /* Bit 24: SRAM2 parity check enable */ -#define FLASH_OPTCR_SRAM2_RST (1 << 25) /* Bit 24: SRAM2 Erase when system reset */ +#define FLASH_OPTCR_SRAM2_RST (1 << 25) /* Bit 25: SRAM2 Erase when system reset */ +#if defined(CONFIG_STM32L4_STM32L4X3) || defined(CONFIG_STM32L4_STM32L496XX) +# define FLASH_OPTCR_NSWBOOT0 (1 << 26) /* Bit 26: Software BOOT0 */ +# define FLASH_OPTCR_NBOOT0 (1 << 27) /* Bit 27: nBOOT0 option bit */ +#endif + #define FLASH_OPTCR_BORLEV_SHIFT (8) /* Bits 8-10: BOR reset Level */ #define FLASH_OPTCR_BORLEV_MASK (7 << FLASH_OPTCR_BORLEV_SHIFT) #define FLASH_OPTCR_VBOR0 (0 << FLASH_OPTCR_BORLEV_SHIFT) /* 000: BOR Level 0 (1.7 V) */ @@ -242,7 +262,7 @@ * Public Functions ************************************************************************************/ -void STM32L4_flash_lock(void); -void STM32L4_flash_unlock(void); +void stm32l4_flash_lock(void); +void stm32l4_flash_unlock(void); -#endif /* __ARCH_ARM_SRC_STM32L4_CHIP_STM32L4_FLASH_H */ +#endif /* __ARCH_ARM_SRC_STM32L4_STM32L4_FLASH_H */