diff --git a/arch/arm/src/stm32/stm32_flash.c b/arch/arm/src/stm32/stm32_flash.c index 529596df65c..f9ec357ac87 100644 --- a/arch/arm/src/stm32/stm32_flash.c +++ b/arch/arm/src/stm32/stm32_flash.c @@ -4,9 +4,6 @@ * Copyright (C) 2011 Uros Platise. All rights reserved. * Author: Uros Platise * - * STM32L1 support: - * Author: Juha Niskanen - * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -38,10 +35,6 @@ /* Provides standard flash access functions, to be used by the flash mtd driver. * The interface is defined in the include/nuttx/progmem.h - * - * Requirements during write/erase operations on FLASH: - * - HSI must be ON. - * - Low Power Modes are not permitted during write/erase */ /************************************************************************************ @@ -49,865 +42,15 @@ ************************************************************************************/ #include -#include -#include -#include -#include -#include - -#include "stm32_flash.h" -#include "stm32_rcc.h" -#include "stm32_waste.h" - -#include "up_arch.h" - -/* Only for the STM32F[1|2|3|4]0xx family and STM32L15xx. */ - -#if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F20XX) || \ - defined(CONFIG_STM32_STM32F30XX) || defined (CONFIG_STM32_STM32F4XXX) || \ - defined(CONFIG_STM32_STM32L15XX) - -#if defined(CONFIG_STM32_FLASH_CONFIG_DEFAULT) && \ - (defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F4XXX)) -# warning "Default Flash Configuration Used - See Override Flash Size Designator" -#endif - -/************************************************************************************ - * Pre-processor Definitions - ************************************************************************************/ +/* Include the correct FLASH implementation for the selection STM32 part */ #if defined(CONFIG_STM32_STM32L15XX) -# define FLASH_KEY1 0x8C9DAEBF -# define FLASH_KEY2 0x13141516 -# define FLASH_OPTKEY1 0xFBEAD9C8 -# define FLASH_OPTKEY2 0x24252627 -# define EEPROM_KEY1 0x89ABCDEF -# define EEPROM_KEY2 0x02030405 +# include "stm32l15xx_flash.c" +#elif defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) +# include "stm32f10xxf30xx_flash.c" +#elif defined(CONFIG_STM32_STM32F20XX) || defined (CONFIG_STM32_STM32F4XXX) +# include "stm32f20xxf40xx_flash.c" #else -# define FLASH_KEY1 0x45670123 -# define FLASH_KEY2 0xCDEF89AB -# define FLASH_OPTKEY1 0x08192A3B -# define FLASH_OPTKEY2 0x4C5D6E7F +# warning "No FLASH support for the selected part" #endif - -#if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) -# define FLASH_CR_PAGE_ERASE FLASH_CR_PER -# define FLASH_SR_WRITE_PROTECTION_ERROR FLASH_SR_WRPRT_ERR -#elif defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F4XXX) -# define FLASH_CR_PAGE_ERASE FLASH_CR_SER -# define FLASH_SR_WRITE_PROTECTION_ERROR FLASH_SR_WRPERR -#elif defined(CONFIG_STM32_STM32L15XX) -# define FLASH_SR_WRITE_PROTECTION_ERROR FLASH_SR_WRPERR -# define FLASH_SR_ALLERRS (FLASH_SR_RDERR | FLASH_SR_SIZERR | \ - FLASH_SR_PGAERR | FLASH_SR_WRPERR) -#endif - -/* STM32L1 internal flash is based on EEPROM-technology while most others - * are NOR-flash, thus many things are different including the erase value. - */ -#if defined(CONFIG_STM32_STM32L15XX) -# define FLASH_ERASEDVALUE 0x00 -#else -# define FLASH_ERASEDVALUE 0xff -#endif - -/************************************************************************************ - * Private Data - ************************************************************************************/ - -static sem_t g_sem = SEM_INITIALIZER(1); - -/************************************************************************************ - * Private Functions - ************************************************************************************/ - -static void sem_lock(void) -{ - int ret; - - do - { - /* Take the semaphore (perhaps waiting) */ - - ret = nxsem_wait(&g_sem); - - /* The only case that an error should occur here is if the wait was - * awakened by a signal. - */ - - DEBUGASSERT(ret == OK || ret == -EINTR); - } - while (ret == -EINTR); -} - -static inline void sem_unlock(void) -{ - nxsem_post(&g_sem); -} - -#if !defined(CONFIG_STM32_STM32L15XX) - -static void flash_unlock(void) -{ - while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) - { - up_waste(); - } - - if (getreg32(STM32_FLASH_CR) & FLASH_CR_LOCK) - { - /* Unlock sequence */ - - putreg32(FLASH_KEY1, STM32_FLASH_KEYR); - putreg32(FLASH_KEY2, STM32_FLASH_KEYR); - } -} - -static void flash_lock(void) -{ - modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_LOCK); -} - -#endif /* !defined(CONFIG_STM32_STM32L15XX) */ - -#if defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) -static void data_cache_disable(void) -{ - modifyreg32(STM32_FLASH_ACR, FLASH_ACR_DCEN, 0); -} - -static void data_cache_enable(void) -{ - /* Reset data cache */ - - modifyreg32(STM32_FLASH_ACR, 0, FLASH_ACR_DCRST); - - /* Enable data cache */ - - modifyreg32(STM32_FLASH_ACR, 0, FLASH_ACR_DCEN); -} -#endif /* defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) */ - -#if defined(CONFIG_STM32_STM32L15XX) - -static void stm32_eeprom_unlock(void) -{ - while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) - { - up_waste(); - } - - if (getreg32(STM32_FLASH_PECR) & FLASH_PECR_PELOCK) - { - /* Unlock sequence */ - - putreg32(EEPROM_KEY1, STM32_FLASH_PEKEYR); - putreg32(EEPROM_KEY2, STM32_FLASH_PEKEYR); - } -} - -static void stm32_eeprom_lock(void) -{ - modifyreg32(STM32_FLASH_PECR, 0, FLASH_PECR_PELOCK); -} - -static void flash_unlock(void) -{ - if (getreg32(STM32_FLASH_PECR) & FLASH_PECR_PRGLOCK) - { - stm32_eeprom_unlock(); - - /* Unlock sequence */ - - putreg32(FLASH_KEY1, STM32_FLASH_PRGKEYR); - putreg32(FLASH_KEY2, STM32_FLASH_PRGKEYR); - } -} - -static void flash_lock(void) -{ - modifyreg32(STM32_FLASH_PECR, 0, FLASH_PECR_PRGLOCK); - stm32_eeprom_lock(); -} - -static ssize_t stm32_eeprom_erase_write(size_t addr, const void *buf, - size_t buflen) -{ - const char *cbuf = buf; - size_t i; - - if (buflen == 0) - { - return 0; - } - - /* Check for valid address range */ - - if (addr >= STM32_EEPROM_BASE) - { - addr -= STM32_EEPROM_BASE; - } - - if (addr >= STM32_EEPROM_SIZE) - { - return -EINVAL; - } - - /* TODO: Voltage range must be range 1 or 2. Erase/program not allowed in - * range 3. - */ - - stm32_eeprom_unlock(); - - /* Clear pending status flags. */ - - putreg32(FLASH_SR_WRPERR | FLASH_SR_PGAERR | - FLASH_SR_SIZERR | FLASH_SR_OPTVERR | - FLASH_SR_OPTVERRUSR | FLASH_SR_RDERR, STM32_FLASH_SR); - - /* Enable automatic erasing (by disabling 'fixed time' programming). */ - - modifyreg32(STM32_FLASH_PECR, FLASH_PECR_FTDW, 0); - - /* Write buffer to EEPROM data memory. */ - - addr += STM32_EEPROM_BASE; - i = 0; - while (i < buflen) - { - uint32_t writeval; - size_t left = buflen - i; - - if ((addr & 0x03) == 0x00 && left >= 4) - { - /* Read/erase/write word */ - - writeval = cbuf ? *(uint32_t *)cbuf : 0; - putreg32(writeval, addr); - } - else if ((addr & 0x01) == 0x00 && left >= 2) - { - /* Read/erase/write half-word */ - - writeval = cbuf ? *(uint16_t *)cbuf : 0; - putreg16(writeval, addr); - } - else - { - /* Read/erase/write byte */ - - writeval = cbuf ? *(uint8_t *)cbuf : 0; - putreg8(writeval, addr); - } - - /* ... and wait to complete. */ - - while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) - { - up_waste(); - } - - /* Verify */ - - /* We do not check Options Byte invalid flags FLASH_SR_OPTVERR - * and FLASH_SR_OPTVERRUSR for EEPROM erase/write. They are unrelated - * and STM32L standard library does not check for these either. - */ - - if (getreg32(STM32_FLASH_SR) & (FLASH_SR_WRPERR | FLASH_SR_PGAERR | - FLASH_SR_SIZERR | FLASH_SR_RDERR)) - { - stm32_eeprom_lock(); - return -EROFS; - } - - if ((addr & 0x03) == 0x00 && left >= 4) - { - if (getreg32(addr) != writeval) - { - stm32_eeprom_lock(); - return -EIO; - } - - addr += 4; - i += 4; - cbuf += !!(cbuf) * 4; - } - else if ((addr & 0x01) == 0x00 && left >= 2) - { - if (getreg16(addr) != writeval) - { - stm32_eeprom_lock(); - return -EIO; - } - - addr += 2; - i += 2; - cbuf += !!(cbuf) * 2; - } - else - { - if (getreg8(addr) != writeval) - { - stm32_eeprom_lock(); - return -EIO; - } - - addr += 1; - i += 1; - cbuf += !!(cbuf) * 1; - } - } - - stm32_eeprom_lock(); - return buflen; -} - -#endif /* defined(CONFIG_STM32_STM32L15XX) */ - -/************************************************************************************ - * Public Functions - ************************************************************************************/ - -void stm32_flash_unlock(void) -{ - sem_lock(); - flash_unlock(); - sem_unlock(); -} - -void stm32_flash_lock(void) -{ - sem_lock(); - flash_lock(); - sem_unlock(); -} - -#if defined(CONFIG_STM32_STM32L15XX) - -size_t stm32_eeprom_size(void) -{ - return STM32_EEPROM_SIZE; -} - -size_t stm32_eeprom_getaddress(void) -{ - return STM32_EEPROM_BASE; -} - -ssize_t stm32_eeprom_write(size_t addr, const void *buf, size_t buflen) -{ - ssize_t outlen; - - if (!buf) - { - return -EINVAL; - } - - sem_lock(); - outlen = stm32_eeprom_erase_write(addr, buf, buflen); - sem_unlock(); - - return outlen; -} - -ssize_t stm32_eeprom_erase(size_t addr, size_t eraselen) -{ - ssize_t outlen; - - sem_lock(); - outlen = stm32_eeprom_erase_write(addr, NULL, eraselen); - sem_unlock(); - - return outlen; -} - -#endif /* defined(CONFIG_STM32_STM32L15XX) */ - -/************************************************************************************ - * Name: stm32_flash_writeprotect - * - * Description: - * Enable or disable the write protection of a flash sector. - * - ************************************************************************************/ - -#ifdef defined(CONFIG_STM32_STM32F20XX) || CONFIG_STM32_STM32F4XXX -int stm32_flash_writeprotect(size_t page, bool enabled) -{ - uint32_t reg; - uint32_t val; - - if (page >= STM32_FLASH_NPAGES) - { - return -EFAULT; - } - - /* Select the register that contains the bit to be changed */ - - if (page < 12) - { - reg = STM32_FLASH_OPTCR; - } -#if defined(CONFIG_STM32_FLASH_CONFIG_I) - else - { - reg = STM32_FLASH_OPTCR1; - page -= 12; - } -#else - else - { - return -EFAULT; - } -#endif - - /* Read the option status */ - - val = getreg32(reg); - - /* Set or clear the protection */ - - if (enabled) - { - val &= ~(1 << (16+page) ); - } - else - { - val |= (1 << (16+page) ); - } - - /* Unlock options */ - - putreg32(FLASH_OPTKEY1, STM32_FLASH_OPTKEYR); - putreg32(FLASH_OPTKEY2, STM32_FLASH_OPTKEYR); - - /* Write options */ - - putreg32(val, reg); - - /* Trigger programming */ - - modifyreg32(STM32_FLASH_OPTCR, 0, FLASH_OPTCR_OPTSTRT); - - /* Wait for completion */ - - while(getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) up_waste(); - - /* Relock options */ - - modifyreg32(STM32_FLASH_OPTCR, 0, FLASH_OPTCR_OPTLOCK); - return 0; -} -#endif - -#if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) || \ - defined(CONFIG_STM32_STM32L15XX) -size_t up_progmem_pagesize(size_t page) -{ - return STM32_FLASH_PAGESIZE; -} - -size_t up_progmem_erasesize(size_t page) -{ - return STM32_FLASH_PAGESIZE; -} - -ssize_t up_progmem_getpage(size_t addr) -{ - if (addr >= STM32_FLASH_BASE) - { - addr -= STM32_FLASH_BASE; - } - - if (addr >= STM32_FLASH_SIZE) - { - return -EFAULT; - } - - return addr / STM32_FLASH_PAGESIZE; -} - -size_t up_progmem_getaddress(size_t page) -{ - if (page >= STM32_FLASH_NPAGES) - { - return SIZE_MAX; - } - - return page * STM32_FLASH_PAGESIZE + STM32_FLASH_BASE; -} - -#endif /* defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) || \ - defined(CONFIG_STM32_STM32L15XX) */ - -#ifdef defined(CONFIG_STM32_STM32F20XX) || CONFIG_STM32_STM32F4XXX -size_t up_progmem_pagesize(size_t page) -{ - static const size_t page_sizes[STM32_FLASH_NPAGES] = STM32_FLASH_SIZES; - - if (page >= sizeof(page_sizes) / sizeof(*page_sizes)) - { - return 0; - } - else - { - return page_sizes[page]; - } -} - -ssize_t up_progmem_getpage(size_t addr) -{ - size_t page_end = 0; - size_t i; - - if (addr >= STM32_FLASH_BASE) - { - addr -= STM32_FLASH_BASE; - } - - if (addr >= STM32_FLASH_SIZE) - { - return -EFAULT; - } - - for (i = 0; i < STM32_FLASH_NPAGES; ++i) - { - page_end += up_progmem_pagesize(i); - if (page_end > addr) - { - return i; - } - } - - return -EFAULT; -} - -size_t up_progmem_getaddress(size_t page) -{ - size_t base_address = STM32_FLASH_BASE; - size_t i; - - if (page >= STM32_FLASH_NPAGES) - { - return SIZE_MAX; - } - - for (i = 0; i < page; ++i) - { - base_address += up_progmem_pagesize(i); - } - - return base_address; -} - -#endif /* defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F4XXX) */ - -size_t up_progmem_npages(void) -{ - return STM32_FLASH_NPAGES; -} - -bool up_progmem_isuniform(void) -{ -#ifdef STM32_FLASH_PAGESIZE - return true; -#else - return false; -#endif -} - -ssize_t up_progmem_ispageerased(size_t page) -{ - size_t addr; - size_t count; - size_t bwritten = 0; - - if (page >= STM32_FLASH_NPAGES) - { - return -EFAULT; - } - - /* Verify */ - - for (addr = up_progmem_getaddress(page), count = up_progmem_pagesize(page); - count; count--, addr++) - { - if (getreg8(addr) != FLASH_ERASEDVALUE) - { - bwritten++; - } - } - - return bwritten; -} - -#if defined(CONFIG_STM32_STM32L15XX) -ssize_t up_progmem_erasepage(size_t page) -{ - size_t page_address; - - if (page >= STM32_FLASH_NPAGES) - { - return -EFAULT; - } - - page_address = up_progmem_getaddress(page); - - /* Get flash ready and begin erasing single page */ - - sem_lock(); - flash_unlock(); - - modifyreg32(STM32_FLASH_PECR, 0, FLASH_PECR_ERASE); - modifyreg32(STM32_FLASH_PECR, 0, FLASH_PECR_PROG); - - /* Erase is started by writing 0x00000000 to the first word - * of the program page. - */ - - putreg32(0x00, page_address); - - while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) - { - up_waste(); - } - - flash_lock(); - sem_unlock(); - - /* Verify */ - - if (up_progmem_ispageerased(page) == 0) - { - return up_progmem_pagesize(page); - } - else - { - return -EIO; - } -} - -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; - - /* STM32L1 requires word access and alignment. */ - - if (addr & 3) - { - return -EINVAL; - } - - if (count & 3) - { - return -EINVAL; - } - - /* Check for valid address range */ - - if (addr >= STM32_FLASH_BASE) - { - addr -= STM32_FLASH_BASE; - } - - if ((addr+count) > STM32_FLASH_SIZE) - { - return -EFAULT; - } - - /* Get flash ready and begin flashing */ - - sem_lock(); - flash_unlock(); - - for (addr += STM32_FLASH_BASE; count; count -= 4, word++, addr += 4) - { - /* Write word and wait to complete */ - - putreg32(*word, addr); - - while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) - { - up_waste(); - } - - /* Verify */ - - if (getreg32(STM32_FLASH_SR) & FLASH_SR_WRITE_PROTECTION_ERROR) - { - ret = -EROFS; - goto out; - } - - if (getreg32(addr) != *word) - { - ret = -EIO; - goto out; - } - } - -out: - /* 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(STM32_FLASH_SR)); - modifyreg32(STM32_FLASH_SR, 0, FLASH_SR_ALLERRS); - } - - flash_lock(); - sem_unlock(); - return (ret == OK) ? written : ret; -} -#else /* !defined(CONFIG_STM32_STM32L15XX) */ -ssize_t up_progmem_erasepage(size_t page) -{ -#if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) - size_t page_address; -#endif - - if (page >= STM32_FLASH_NPAGES) - { - return -EFAULT; - } - - sem_lock(); - -#if !defined(CONFIG_STM32_STM32F20XX) && !defined(CONFIG_STM32_STM32F4XXX) - if (!(getreg32(STM32_RCC_CR) & RCC_CR_HSION)) - { - sem_unlock(); - return -EPERM; - } -#endif - - /* Get flash ready and begin erasing single page */ - - flash_unlock(); - - modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_PAGE_ERASE); - -#if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) - /* Must be valid - page index checked above */ - - page_address = up_progmem_getaddress(page); - putreg32(page_address, STM32_FLASH_AR); - -#elif defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F4XXX) - modifyreg32(STM32_FLASH_CR, FLASH_CR_SNB_MASK, FLASH_CR_SNB(page)); -#endif - - modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_STRT); - - while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) up_waste(); - - modifyreg32(STM32_FLASH_CR, FLASH_CR_PAGE_ERASE, 0); - sem_unlock(); - - /* Verify */ - if (up_progmem_ispageerased(page) == 0) - { - return up_progmem_pagesize(page); /* success */ - } - else - { - return -EIO; /* failure */ - } -} - -ssize_t up_progmem_write(size_t addr, const void *buf, size_t count) -{ - uint16_t *hword = (uint16_t *)buf; - size_t written = count; - - /* STM32 requires half-word access */ - - if (count & 1) - { - return -EINVAL; - } - - /* Check for valid address range */ - - if (addr >= STM32_FLASH_BASE) - { - addr -= STM32_FLASH_BASE; - } - - if ((addr+count) > STM32_FLASH_SIZE) - { - return -EFAULT; - } - - sem_lock(); - -#if !defined(CONFIG_STM32_STM32F20XX) && !defined(CONFIG_STM32_STM32F4XXX) - if (!(getreg32(STM32_RCC_CR) & RCC_CR_HSION)) - { - sem_unlock(); - return -EPERM; - } -#endif - - /* Get flash ready and begin flashing */ - - flash_unlock(); - -#if defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) - data_cache_disable(); -#endif - - modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_PG); - -#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F4XXX) - /* TODO: implement up_progmem_write() to support other sizes than 16-bits */ - - modifyreg32(STM32_FLASH_CR, FLASH_CR_PSIZE_MASK, FLASH_CR_PSIZE_X16); -#endif - - for (addr += STM32_FLASH_BASE; count; count -= 2, hword++, addr += 2) - { - /* Write half-word and wait to complete */ - - putreg16(*hword, addr); - - while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) up_waste(); - - /* Verify */ - - if (getreg32(STM32_FLASH_SR) & FLASH_SR_WRITE_PROTECTION_ERROR) - { - modifyreg32(STM32_FLASH_CR, FLASH_CR_PG, 0); - sem_unlock(); - return -EROFS; - } - - if (getreg16(addr) != *hword) - { - modifyreg32(STM32_FLASH_CR, FLASH_CR_PG, 0); - sem_unlock(); - return -EIO; - } - } - - modifyreg32(STM32_FLASH_CR, FLASH_CR_PG, 0); - -#if defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) - data_cache_enable(); -#endif - - sem_unlock(); - return written; -} -#endif /* !defined(CONFIG_STM32_STM32L15XX) */ - -#endif /* defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F20XX) || \ - * defined(CONFIG_STM32_STM32F30XX) || defined(CONFIG_STM32_STM32F4XXX) || \ - * defined(CONFIG_STM32_STM32L15XX) */ diff --git a/arch/arm/src/stm32/stm32f10xxf30xx_flash.c b/arch/arm/src/stm32/stm32f10xxf30xx_flash.c new file mode 100644 index 00000000000..f137e55ccb3 --- /dev/null +++ b/arch/arm/src/stm32/stm32f10xxf30xx_flash.c @@ -0,0 +1,365 @@ +/************************************************************************************ + * arch/arm/src/stm32/stm3210xxf30xx_flash.c + * + * Copyright (C) 2011 Uros Platise. All rights reserved. + * Author: Uros Platise + * + * 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 + * + * Requirements during write/erase operations on FLASH: + * - HSI must be ON. + * - Low Power Modes are not permitted during write/erase + */ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include "stm32_flash.h" +#include "stm32_rcc.h" +#include "stm32_waste.h" + +#include "up_arch.h" + +/* Only for the STM32F[1|3]0xx family. */ + +#if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#define FLASH_KEY1 0x45670123 +#define FLASH_KEY2 0xcdef89ab +#define FLASH_OPTKEY1 0x08192a3b +#define FLASH_OPTKEY2 0x4c5d6e7f +#define FLASH_ERASEDVALUE 0xff + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +static sem_t g_sem = SEM_INITIALIZER(1); + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +static void sem_lock(void) +{ + int ret; + + do + { + /* Take the semaphore (perhaps waiting) */ + + ret = nxsem_wait(&g_sem); + + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); +} + +static inline void sem_unlock(void) +{ + nxsem_post(&g_sem); +} + +static void flash_unlock(void) +{ + while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) + { + up_waste(); + } + + if (getreg32(STM32_FLASH_CR) & FLASH_CR_LOCK) + { + /* Unlock sequence */ + + putreg32(FLASH_KEY1, STM32_FLASH_KEYR); + putreg32(FLASH_KEY2, STM32_FLASH_KEYR); + } +} + +static void flash_lock(void) +{ + modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_LOCK); +} + +#if defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) +static void data_cache_disable(void) +{ + modifyreg32(STM32_FLASH_ACR, FLASH_ACR_DCEN, 0); +} + +static void data_cache_enable(void) +{ + /* Reset data cache */ + + modifyreg32(STM32_FLASH_ACR, 0, FLASH_ACR_DCRST); + + /* Enable data cache */ + + modifyreg32(STM32_FLASH_ACR, 0, FLASH_ACR_DCEN); +} +#endif /* defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) */ + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +void stm32_flash_unlock(void) +{ + sem_lock(); + flash_unlock(); + sem_unlock(); +} + +void stm32_flash_lock(void) +{ + sem_lock(); + flash_lock(); + sem_unlock(); +} + +size_t up_progmem_pagesize(size_t page) +{ + return STM32_FLASH_PAGESIZE; +} + +size_t up_progmem_erasesize(size_t page) +{ + return STM32_FLASH_PAGESIZE; +} + +ssize_t up_progmem_getpage(size_t addr) +{ + if (addr >= STM32_FLASH_BASE) + { + addr -= STM32_FLASH_BASE; + } + + if (addr >= STM32_FLASH_SIZE) + { + return -EFAULT; + } + + return addr / STM32_FLASH_PAGESIZE; +} + +size_t up_progmem_getaddress(size_t page) +{ + if (page >= STM32_FLASH_NPAGES) + { + return SIZE_MAX; + } + + return page * STM32_FLASH_PAGESIZE + STM32_FLASH_BASE; +} + +size_t up_progmem_npages(void) +{ + return STM32_FLASH_NPAGES; +} + +bool up_progmem_isuniform(void) +{ +#ifdef STM32_FLASH_PAGESIZE + return true; +#else + return false; +#endif +} + +ssize_t up_progmem_ispageerased(size_t page) +{ + size_t addr; + size_t count; + size_t bwritten = 0; + + if (page >= STM32_FLASH_NPAGES) + { + return -EFAULT; + } + + /* Verify */ + + for (addr = up_progmem_getaddress(page), count = up_progmem_pagesize(page); + count; count--, addr++) + { + if (getreg8(addr) != FLASH_ERASEDVALUE) + { + bwritten++; + } + } + + return bwritten; +} + +ssize_t up_progmem_erasepage(size_t page) +{ + size_t page_address; + + if (page >= STM32_FLASH_NPAGES) + { + return -EFAULT; + } + + sem_lock(); + + if (!(getreg32(STM32_RCC_CR) & RCC_CR_HSION)) + { + sem_unlock(); + return -EPERM; + } + + /* Get flash ready and begin erasing single page */ + + flash_unlock(); + + modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_PER); + + /* Must be valid - page index checked above */ + + page_address = up_progmem_getaddress(page); + putreg32(page_address, STM32_FLASH_AR); + + modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_STRT); + + while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) up_waste(); + + modifyreg32(STM32_FLASH_CR, FLASH_CR_PER, 0); + sem_unlock(); + + /* Verify */ + + if (up_progmem_ispageerased(page) == 0) + { + return up_progmem_pagesize(page); /* success */ + } + else + { + return -EIO; /* failure */ + } +} + +ssize_t up_progmem_write(size_t addr, const void *buf, size_t count) +{ + uint16_t *hword = (uint16_t *)buf; + size_t written = count; + + /* STM32 requires half-word access */ + + if (count & 1) + { + return -EINVAL; + } + + /* Check for valid address range */ + + if (addr >= STM32_FLASH_BASE) + { + addr -= STM32_FLASH_BASE; + } + + if ((addr+count) > STM32_FLASH_SIZE) + { + return -EFAULT; + } + + sem_lock(); + + if (!(getreg32(STM32_RCC_CR) & RCC_CR_HSION)) + { + sem_unlock(); + return -EPERM; + } + + /* Get flash ready and begin flashing */ + + flash_unlock(); + +#if defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) + data_cache_disable(); +#endif + + modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_PG); + + for (addr += STM32_FLASH_BASE; count; count -= 2, hword++, addr += 2) + { + /* Write half-word and wait to complete */ + + putreg16(*hword, addr); + + while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) up_waste(); + + /* Verify */ + + if (getreg32(STM32_FLASH_SR) & FLASH_SR_WRPRT_ERR) + { + modifyreg32(STM32_FLASH_CR, FLASH_CR_PG, 0); + sem_unlock(); + return -EROFS; + } + + if (getreg16(addr) != *hword) + { + modifyreg32(STM32_FLASH_CR, FLASH_CR_PG, 0); + sem_unlock(); + return -EIO; + } + } + + modifyreg32(STM32_FLASH_CR, FLASH_CR_PG, 0); + +#if defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) + data_cache_enable(); +#endif + + sem_unlock(); + return written; +} + +#endif /* defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) */ diff --git a/arch/arm/src/stm32/stm32f20xxf40xx_flash.c b/arch/arm/src/stm32/stm32f20xxf40xx_flash.c new file mode 100644 index 00000000000..5d262a96cdc --- /dev/null +++ b/arch/arm/src/stm32/stm32f20xxf40xx_flash.c @@ -0,0 +1,453 @@ +/************************************************************************************ + * arch/arm/src/stm32/stm32f20xx40xx_flash.c + * + * Copyright (C) 2011 Uros Platise. All rights reserved. + * Author: Uros Platise + * + * 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 + * + * Requirements during write/erase operations on FLASH: + * - HSI must be ON. + * - Low Power Modes are not permitted during write/erase + */ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include "stm32_flash.h" +#include "stm32_rcc.h" +#include "stm32_waste.h" + +#include "up_arch.h" + +/* Only for the STM32F[2|4]0xx family. */ + +#if defined(CONFIG_STM32_STM32F20XX) || defined (CONFIG_STM32_STM32F4XXX) + +#if defined(CONFIG_STM32_FLASH_CONFIG_DEFAULT) +# warning "Default Flash Configuration Used - See Override Flash Size Designator" +#endif + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#define FLASH_KEY1 0x45670123 +#define FLASH_KEY2 0xcdef89ab +#define FLASH_OPTKEY1 0x08192a3b +#define FLASH_OPTKEY2 0x4c5d6e7f +#define FLASH_ERASEDVALUE 0xff + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +static sem_t g_sem = SEM_INITIALIZER(1); + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +static void sem_lock(void) +{ + int ret; + + do + { + /* Take the semaphore (perhaps waiting) */ + + ret = nxsem_wait(&g_sem); + + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); +} + +static inline void sem_unlock(void) +{ + nxsem_post(&g_sem); +} + +static void flash_unlock(void) +{ + while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) + { + up_waste(); + } + + if (getreg32(STM32_FLASH_CR) & FLASH_CR_LOCK) + { + /* Unlock sequence */ + + putreg32(FLASH_KEY1, STM32_FLASH_KEYR); + putreg32(FLASH_KEY2, STM32_FLASH_KEYR); + } +} + +static void flash_lock(void) +{ + modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_LOCK); +} + +#if defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) +static void data_cache_disable(void) +{ + modifyreg32(STM32_FLASH_ACR, FLASH_ACR_DCEN, 0); +} + +static void data_cache_enable(void) +{ + /* Reset data cache */ + + modifyreg32(STM32_FLASH_ACR, 0, FLASH_ACR_DCRST); + + /* Enable data cache */ + + modifyreg32(STM32_FLASH_ACR, 0, FLASH_ACR_DCEN); +} +#endif /* defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) */ + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +void stm32_flash_unlock(void) +{ + sem_lock(); + flash_unlock(); + sem_unlock(); +} + +void stm32_flash_lock(void) +{ + sem_lock(); + flash_lock(); + sem_unlock(); +} + +/************************************************************************************ + * Name: stm32_flash_writeprotect + * + * Description: + * Enable or disable the write protection of a flash sector. + * + ************************************************************************************/ + +int stm32_flash_writeprotect(size_t page, bool enabled) +{ + uint32_t reg; + uint32_t val; + + if (page >= STM32_FLASH_NPAGES) + { + return -EFAULT; + } + + /* Select the register that contains the bit to be changed */ + + if (page < 12) + { + reg = STM32_FLASH_OPTCR; + } +#if defined(CONFIG_STM32_FLASH_CONFIG_I) + else + { + reg = STM32_FLASH_OPTCR1; + page -= 12; + } +#else + else + { + return -EFAULT; + } +#endif + + /* Read the option status */ + + val = getreg32(reg); + + /* Set or clear the protection */ + + if (enabled) + { + val &= ~(1 << (16+page) ); + } + else + { + val |= (1 << (16+page) ); + } + + /* Unlock options */ + + putreg32(FLASH_OPTKEY1, STM32_FLASH_OPTKEYR); + putreg32(FLASH_OPTKEY2, STM32_FLASH_OPTKEYR); + + /* Write options */ + + putreg32(val, reg); + + /* Trigger programming */ + + modifyreg32(STM32_FLASH_OPTCR, 0, FLASH_OPTCR_OPTSTRT); + + /* Wait for completion */ + + while(getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) up_waste(); + + /* Relock options */ + + modifyreg32(STM32_FLASH_OPTCR, 0, FLASH_OPTCR_OPTLOCK); + return 0; +} + +size_t up_progmem_pagesize(size_t page) +{ + static const size_t page_sizes[STM32_FLASH_NPAGES] = STM32_FLASH_SIZES; + + if (page >= sizeof(page_sizes) / sizeof(*page_sizes)) + { + return 0; + } + else + { + return page_sizes[page]; + } +} + +ssize_t up_progmem_getpage(size_t addr) +{ + size_t page_end = 0; + size_t i; + + if (addr >= STM32_FLASH_BASE) + { + addr -= STM32_FLASH_BASE; + } + + if (addr >= STM32_FLASH_SIZE) + { + return -EFAULT; + } + + for (i = 0; i < STM32_FLASH_NPAGES; ++i) + { + page_end += up_progmem_pagesize(i); + if (page_end > addr) + { + return i; + } + } + + return -EFAULT; +} + +size_t up_progmem_getaddress(size_t page) +{ + size_t base_address = STM32_FLASH_BASE; + size_t i; + + if (page >= STM32_FLASH_NPAGES) + { + return SIZE_MAX; + } + + for (i = 0; i < page; ++i) + { + base_address += up_progmem_pagesize(i); + } + + return base_address; +} + +size_t up_progmem_npages(void) +{ + return STM32_FLASH_NPAGES; +} + +bool up_progmem_isuniform(void) +{ +#ifdef STM32_FLASH_PAGESIZE + return true; +#else + return false; +#endif +} + +ssize_t up_progmem_ispageerased(size_t page) +{ + size_t addr; + size_t count; + size_t bwritten = 0; + + if (page >= STM32_FLASH_NPAGES) + { + return -EFAULT; + } + + /* Verify */ + + for (addr = up_progmem_getaddress(page), count = up_progmem_pagesize(page); + count; count--, addr++) + { + if (getreg8(addr) != FLASH_ERASEDVALUE) + { + bwritten++; + } + } + + return bwritten; +} + +ssize_t up_progmem_erasepage(size_t page) +{ + if (page >= STM32_FLASH_NPAGES) + { + return -EFAULT; + } + + sem_lock(); + + /* Get flash ready and begin erasing single page */ + + flash_unlock(); + + modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_SER); + modifyreg32(STM32_FLASH_CR, FLASH_CR_SNB_MASK, FLASH_CR_SNB(page)); + modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_STRT); + + while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) up_waste(); + + modifyreg32(STM32_FLASH_CR, FLASH_CR_SER, 0); + sem_unlock(); + + /* Verify */ + + if (up_progmem_ispageerased(page) == 0) + { + return up_progmem_pagesize(page); /* success */ + } + else + { + return -EIO; /* failure */ + } +} + +ssize_t up_progmem_write(size_t addr, const void *buf, size_t count) +{ + uint16_t *hword = (uint16_t *)buf; + size_t written = count; + + /* STM32 requires half-word access */ + + if (count & 1) + { + return -EINVAL; + } + + /* Check for valid address range */ + + if (addr >= STM32_FLASH_BASE) + { + addr -= STM32_FLASH_BASE; + } + + if ((addr+count) > STM32_FLASH_SIZE) + { + return -EFAULT; + } + + sem_lock(); + + /* Get flash ready and begin flashing */ + + flash_unlock(); + +#if defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) + data_cache_disable(); +#endif + + modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_PG); + + /* TODO: implement up_progmem_write() to support other sizes than 16-bits */ + + modifyreg32(STM32_FLASH_CR, FLASH_CR_PSIZE_MASK, FLASH_CR_PSIZE_X16); + + for (addr += STM32_FLASH_BASE; count; count -= 2, hword++, addr += 2) + { + /* Write half-word and wait to complete */ + + putreg16(*hword, addr); + + while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) up_waste(); + + /* Verify */ + + if (getreg32(STM32_FLASH_SR) & FLASH_CR_SER) + { + modifyreg32(STM32_FLASH_CR, FLASH_CR_PG, 0); + sem_unlock(); + return -EROFS; + } + + if (getreg16(addr) != *hword) + { + modifyreg32(STM32_FLASH_CR, FLASH_CR_PG, 0); + sem_unlock(); + return -EIO; + } + } + + modifyreg32(STM32_FLASH_CR, FLASH_CR_PG, 0); + +#if defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) + data_cache_enable(); +#endif + + sem_unlock(); + return written; +} + +#endif /* defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F4XXX) */ diff --git a/arch/arm/src/stm32/stm32l15xx_flash.c b/arch/arm/src/stm32/stm32l15xx_flash.c new file mode 100644 index 00000000000..f1d68f6a4fd --- /dev/null +++ b/arch/arm/src/stm32/stm32l15xx_flash.c @@ -0,0 +1,566 @@ +/************************************************************************************ + * arch/arm/src/stm32/stm3l15xx_flash.c + * + * Copyright (C) 2011 Uros Platise. All rights reserved. + * Author: Uros Platise + * + * STM32L1 support: + * Author: 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 + * + * Requirements during write/erase operations on FLASH: + * - HSI must be ON. + * - Low Power Modes are not permitted during write/erase + */ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include "stm32_flash.h" +#include "stm32_rcc.h" +#include "stm32_waste.h" + +#include "up_arch.h" + +/* Only for the STM32L15xx family. */ + +#if defined(CONFIG_STM32_STM32L15XX) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#define FLASH_KEY1 0x8c9daebf +#define FLASH_KEY2 0x13141516 +#define FLASH_OPTKEY1 0xfbead9c8 +#define FLASH_OPTKEY2 0x24252627 +#define EEPROM_KEY1 0x89abcdef +#define EEPROM_KEY2 0x02030405 + +#define FLASH_SR_WRITE_PROTECTION_ERROR FLASH_SR_WRPERR +#define FLASH_SR_ALLERRS (FLASH_SR_RDERR | FLASH_SR_SIZERR | \ + FLASH_SR_PGAERR | FLASH_SR_WRPERR) + +/* STM32L1 internal flash is based on EEPROM-technology while most others + * are NOR-flash, thus many things are different including the erase value. + */ + +#define FLASH_ERASEDVALUE 0x00 + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +static sem_t g_sem = SEM_INITIALIZER(1); + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +static void sem_lock(void) +{ + int ret; + + do + { + /* Take the semaphore (perhaps waiting) */ + + ret = nxsem_wait(&g_sem); + + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); +} + +static inline void sem_unlock(void) +{ + nxsem_post(&g_sem); +} + +#if defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) +static void data_cache_disable(void) +{ + modifyreg32(STM32_FLASH_ACR, FLASH_ACR_DCEN, 0); +} + +static void data_cache_enable(void) +{ + /* Reset data cache */ + + modifyreg32(STM32_FLASH_ACR, 0, FLASH_ACR_DCRST); + + /* Enable data cache */ + + modifyreg32(STM32_FLASH_ACR, 0, FLASH_ACR_DCEN); +} +#endif /* defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) */ + +static void stm32_eeprom_unlock(void) +{ + while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) + { + up_waste(); + } + + if (getreg32(STM32_FLASH_PECR) & FLASH_PECR_PELOCK) + { + /* Unlock sequence */ + + putreg32(EEPROM_KEY1, STM32_FLASH_PEKEYR); + putreg32(EEPROM_KEY2, STM32_FLASH_PEKEYR); + } +} + +static void stm32_eeprom_lock(void) +{ + modifyreg32(STM32_FLASH_PECR, 0, FLASH_PECR_PELOCK); +} + +static void flash_unlock(void) +{ + if (getreg32(STM32_FLASH_PECR) & FLASH_PECR_PRGLOCK) + { + stm32_eeprom_unlock(); + + /* Unlock sequence */ + + putreg32(FLASH_KEY1, STM32_FLASH_PRGKEYR); + putreg32(FLASH_KEY2, STM32_FLASH_PRGKEYR); + } +} + +static void flash_lock(void) +{ + modifyreg32(STM32_FLASH_PECR, 0, FLASH_PECR_PRGLOCK); + stm32_eeprom_lock(); +} + +static ssize_t stm32_eeprom_erase_write(size_t addr, const void *buf, + size_t buflen) +{ + const char *cbuf = buf; + size_t i; + + if (buflen == 0) + { + return 0; + } + + /* Check for valid address range */ + + if (addr >= STM32_EEPROM_BASE) + { + addr -= STM32_EEPROM_BASE; + } + + if (addr >= STM32_EEPROM_SIZE) + { + return -EINVAL; + } + + /* TODO: Voltage range must be range 1 or 2. Erase/program not allowed in + * range 3. + */ + + stm32_eeprom_unlock(); + + /* Clear pending status flags. */ + + putreg32(FLASH_SR_WRPERR | FLASH_SR_PGAERR | + FLASH_SR_SIZERR | FLASH_SR_OPTVERR | + FLASH_SR_OPTVERRUSR | FLASH_SR_RDERR, STM32_FLASH_SR); + + /* Enable automatic erasing (by disabling 'fixed time' programming). */ + + modifyreg32(STM32_FLASH_PECR, FLASH_PECR_FTDW, 0); + + /* Write buffer to EEPROM data memory. */ + + addr += STM32_EEPROM_BASE; + i = 0; + while (i < buflen) + { + uint32_t writeval; + size_t left = buflen - i; + + if ((addr & 0x03) == 0x00 && left >= 4) + { + /* Read/erase/write word */ + + writeval = cbuf ? *(uint32_t *)cbuf : 0; + putreg32(writeval, addr); + } + else if ((addr & 0x01) == 0x00 && left >= 2) + { + /* Read/erase/write half-word */ + + writeval = cbuf ? *(uint16_t *)cbuf : 0; + putreg16(writeval, addr); + } + else + { + /* Read/erase/write byte */ + + writeval = cbuf ? *(uint8_t *)cbuf : 0; + putreg8(writeval, addr); + } + + /* ... and wait to complete. */ + + while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) + { + up_waste(); + } + + /* Verify */ + + /* We do not check Options Byte invalid flags FLASH_SR_OPTVERR + * and FLASH_SR_OPTVERRUSR for EEPROM erase/write. They are unrelated + * and STM32L standard library does not check for these either. + */ + + if (getreg32(STM32_FLASH_SR) & (FLASH_SR_WRPERR | FLASH_SR_PGAERR | + FLASH_SR_SIZERR | FLASH_SR_RDERR)) + { + stm32_eeprom_lock(); + return -EROFS; + } + + if ((addr & 0x03) == 0x00 && left >= 4) + { + if (getreg32(addr) != writeval) + { + stm32_eeprom_lock(); + return -EIO; + } + + addr += 4; + i += 4; + cbuf += !!(cbuf) * 4; + } + else if ((addr & 0x01) == 0x00 && left >= 2) + { + if (getreg16(addr) != writeval) + { + stm32_eeprom_lock(); + return -EIO; + } + + addr += 2; + i += 2; + cbuf += !!(cbuf) * 2; + } + else + { + if (getreg8(addr) != writeval) + { + stm32_eeprom_lock(); + return -EIO; + } + + addr += 1; + i += 1; + cbuf += !!(cbuf) * 1; + } + } + + stm32_eeprom_lock(); + return buflen; +} + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +void stm32_flash_unlock(void) +{ + sem_lock(); + flash_unlock(); + sem_unlock(); +} + +void stm32_flash_lock(void) +{ + sem_lock(); + flash_lock(); + sem_unlock(); +} + +size_t stm32_eeprom_size(void) +{ + return STM32_EEPROM_SIZE; +} + +size_t stm32_eeprom_getaddress(void) +{ + return STM32_EEPROM_BASE; +} + +ssize_t stm32_eeprom_write(size_t addr, const void *buf, size_t buflen) +{ + ssize_t outlen; + + if (!buf) + { + return -EINVAL; + } + + sem_lock(); + outlen = stm32_eeprom_erase_write(addr, buf, buflen); + sem_unlock(); + + return outlen; +} + +ssize_t stm32_eeprom_erase(size_t addr, size_t eraselen) +{ + ssize_t outlen; + + sem_lock(); + outlen = stm32_eeprom_erase_write(addr, NULL, eraselen); + sem_unlock(); + + return outlen; +} + +size_t up_progmem_pagesize(size_t page) +{ + return STM32_FLASH_PAGESIZE; +} + +size_t up_progmem_erasesize(size_t page) +{ + return STM32_FLASH_PAGESIZE; +} + +ssize_t up_progmem_getpage(size_t addr) +{ + if (addr >= STM32_FLASH_BASE) + { + addr -= STM32_FLASH_BASE; + } + + if (addr >= STM32_FLASH_SIZE) + { + return -EFAULT; + } + + return addr / STM32_FLASH_PAGESIZE; +} + +size_t up_progmem_getaddress(size_t page) +{ + if (page >= STM32_FLASH_NPAGES) + { + return SIZE_MAX; + } + + return page * STM32_FLASH_PAGESIZE + STM32_FLASH_BASE; +} + +size_t up_progmem_npages(void) +{ + return STM32_FLASH_NPAGES; +} + +bool up_progmem_isuniform(void) +{ +#ifdef STM32_FLASH_PAGESIZE + return true; +#else + return false; +#endif +} + +ssize_t up_progmem_ispageerased(size_t page) +{ + size_t addr; + size_t count; + size_t bwritten = 0; + + if (page >= STM32_FLASH_NPAGES) + { + return -EFAULT; + } + + /* Verify */ + + for (addr = up_progmem_getaddress(page), count = up_progmem_pagesize(page); + count; count--, addr++) + { + if (getreg8(addr) != FLASH_ERASEDVALUE) + { + bwritten++; + } + } + + return bwritten; +} + +ssize_t up_progmem_erasepage(size_t page) +{ + size_t page_address; + + if (page >= STM32_FLASH_NPAGES) + { + return -EFAULT; + } + + page_address = up_progmem_getaddress(page); + + /* Get flash ready and begin erasing single page */ + + sem_lock(); + flash_unlock(); + + modifyreg32(STM32_FLASH_PECR, 0, FLASH_PECR_ERASE); + modifyreg32(STM32_FLASH_PECR, 0, FLASH_PECR_PROG); + + /* Erase is started by writing 0x00000000 to the first word + * of the program page. + */ + + putreg32(0x00, page_address); + + while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) + { + up_waste(); + } + + flash_lock(); + sem_unlock(); + + /* Verify */ + + if (up_progmem_ispageerased(page) == 0) + { + return up_progmem_pagesize(page); + } + else + { + return -EIO; + } +} + +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; + + /* STM32L1 requires word access and alignment. */ + + if (addr & 3) + { + return -EINVAL; + } + + if (count & 3) + { + return -EINVAL; + } + + /* Check for valid address range */ + + if (addr >= STM32_FLASH_BASE) + { + addr -= STM32_FLASH_BASE; + } + + if ((addr+count) > STM32_FLASH_SIZE) + { + return -EFAULT; + } + + /* Get flash ready and begin flashing */ + + sem_lock(); + flash_unlock(); + + for (addr += STM32_FLASH_BASE; count; count -= 4, word++, addr += 4) + { + /* Write word and wait to complete */ + + putreg32(*word, addr); + + while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) + { + up_waste(); + } + + /* Verify */ + + if (getreg32(STM32_FLASH_SR) & FLASH_SR_WRITE_PROTECTION_ERROR) + { + ret = -EROFS; + goto out; + } + + if (getreg32(addr) != *word) + { + ret = -EIO; + goto out; + } + } + +out: + /* 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(STM32_FLASH_SR)); + modifyreg32(STM32_FLASH_SR, 0, FLASH_SR_ALLERRS); + } + + flash_lock(); + sem_unlock(); + return (ret == OK) ? written : ret; +} +#endif /* defined(CONFIG_STM32_STM32L15XX) */