| /**************************************************************************** |
| * arch/arm/src/stm32/stm32f20xxf40xx_flash.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /* 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 <nuttx/config.h> |
| #include <nuttx/arch.h> |
| #include <nuttx/mutex.h> |
| |
| #include <stdbool.h> |
| #include <assert.h> |
| #include <errno.h> |
| |
| #include "stm32_flash.h" |
| #include "stm32_rcc.h" |
| #include "stm32_waste.h" |
| #include "arm_internal.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 mutex_t g_lock = NXMUTEX_INITIALIZER; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| static void flash_unlock(void) |
| { |
| while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) |
| { |
| stm32_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 |
| ****************************************************************************/ |
| |
| int stm32_flash_unlock(void) |
| { |
| int ret; |
| |
| ret = nxmutex_lock(&g_lock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| flash_unlock(); |
| nxmutex_unlock(&g_lock); |
| |
| return ret; |
| } |
| |
| int stm32_flash_lock(void) |
| { |
| int ret; |
| |
| ret = nxmutex_lock(&g_lock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| flash_lock(); |
| nxmutex_unlock(&g_lock); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * 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) |
| { |
| stm32_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]; |
| } |
| } |
| |
| size_t up_progmem_erasesize(size_t block) |
| { |
| return up_progmem_pagesize(block); |
| } |
| |
| 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_neraseblocks(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_eraseblock(size_t block) |
| { |
| if (block >= STM32_FLASH_NPAGES) |
| { |
| return -EFAULT; |
| } |
| |
| nxmutex_lock(&g_lock); |
| |
| /* Get flash ready and begin erasing single block */ |
| |
| flash_unlock(); |
| |
| modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_SER); |
| modifyreg32(STM32_FLASH_CR, FLASH_CR_SNB_MASK, FLASH_CR_SNB(block)); |
| modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_STRT); |
| |
| while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) |
| { |
| stm32_waste(); |
| } |
| |
| modifyreg32(STM32_FLASH_CR, FLASH_CR_SER, 0); |
| nxmutex_unlock(&g_lock); |
| |
| /* Verify */ |
| |
| if (up_progmem_ispageerased(block) == 0) |
| { |
| return up_progmem_pagesize(block); /* 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; |
| } |
| |
| nxmutex_lock(&g_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) |
| { |
| stm32_waste(); |
| } |
| |
| /* Verify */ |
| |
| if (getreg32(STM32_FLASH_SR) & FLASH_CR_SER) |
| { |
| modifyreg32(STM32_FLASH_CR, FLASH_CR_PG, 0); |
| nxmutex_unlock(&g_lock); |
| return -EROFS; |
| } |
| |
| if (getreg16(addr) != *hword) |
| { |
| modifyreg32(STM32_FLASH_CR, FLASH_CR_PG, 0); |
| nxmutex_unlock(&g_lock); |
| 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 |
| |
| nxmutex_unlock(&g_lock); |
| return written; |
| } |
| |
| uint8_t up_progmem_erasestate(void) |
| { |
| return FLASH_ERASEDVALUE; |
| } |
| |
| #endif /* defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F4XXX) */ |