diff --git a/arch/arm/src/samv7/sam_progmem.c b/arch/arm/src/samv7/sam_progmem.c index f2ba6d14f64..552c1ed609e 100644 --- a/arch/arm/src/samv7/sam_progmem.c +++ b/arch/arm/src/samv7/sam_progmem.c @@ -67,7 +67,7 @@ /* All sectors are 128KB and are uniform in size. * The only execption is sector 0 which is subdivided into two small sectors * of 8KB and one larger sector of 112KB. - * The page size is 515 bytes. However, the smallest thing that can be + * The page size is 512 bytes. However, the smallest thing that can be * erased is four pages. We will refer to this as a "cluster". */ @@ -577,7 +577,7 @@ ssize_t up_progmem_ispageerased(size_t cluster) return -EFAULT; } - /* Flush and invalidate nvalidate D-Cache for this address range */ + /* Flush and invalidate D-Cache for this address range */ address = (cluster << SAMV7_CLUSTER_SHIFT) + SAMV7_PROGMEM_START; arch_flush_dcache(address, address + SAMV7_CLUSTER_SIZE); diff --git a/arch/arm/src/stm32l4/stm32l4_flash.c b/arch/arm/src/stm32l4/stm32l4_flash.c index fdafc0c7ec1..c706b50d93d 100644 --- a/arch/arm/src/stm32l4/stm32l4_flash.c +++ b/arch/arm/src/stm32l4/stm32l4_flash.c @@ -55,6 +55,7 @@ #include #include #include +#include #include "stm32l4_rcc.h" #include "stm32l4_waste.h" @@ -75,26 +76,37 @@ * Pre-processor Definitions ************************************************************************************/ -#define FLASH_KEY1 0x45670123 -#define FLASH_KEY2 0xCDEF89AB +#define FLASH_KEY1 0x45670123 +#define FLASH_KEY2 0xCDEF89AB -#define OPTBYTES_KEY1 0x08192A3B -#define OPTBYTES_KEY2 0x4C5D6E7F +#define OPTBYTES_KEY1 0x08192A3B +#define OPTBYTES_KEY2 0x4C5D6E7F + +#define FLASH_PAGE_SIZE STM32L4_FLASH_PAGESIZE +#define FLASH_PAGE_WORDS (FLASH_PAGE_SIZE / 4) +#define FLASH_PAGE_MASK (FLASH_PAGE_SIZE - 1) +#define FLASH_PAGE_SHIFT (11) /* 2**11 = 2048B */ +#define FLASH_BYTE2PAGE(o) ((o) >> FLASH_PAGE_SHIFT) #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) +#define FLASH_SR_ALLERRS (FLASH_SR_PGSERR | FLASH_SR_SIZERR | \ + FLASH_SR_PGAERR | FLASH_SR_WRPERR | \ + FLASH_SR_PROGERR) + +#ifndef MIN +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif /************************************************************************************ * Private Data ************************************************************************************/ static sem_t g_sem = SEM_INITIALIZER(1); +static uint32_t g_page_buffer[FLASH_PAGE_WORDS]; /************************************************************************************ * Private Functions @@ -167,6 +179,22 @@ static inline void flash_optbytes_lock(void) flash_lock(); } +static inline void flash_erase(size_t page) +{ + finfo("erase page %u\n", page); + + 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); +} + /************************************************************************************ * Public Functions ************************************************************************************/ @@ -290,22 +318,12 @@ ssize_t up_progmem_erasepage(size_t page) return -EFAULT; } + /* Erase single page */ + 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_erase(page); flash_lock(); sem_unlock(); @@ -347,90 +365,133 @@ ssize_t up_progmem_ispageerased(size_t page) return bwritten; } -ssize_t up_progmem_write(size_t addr, const void *buf, size_t count) +ssize_t up_progmem_write(size_t addr, const void *buf, size_t buflen) { - uint32_t *word = (uint32_t *)buf; - size_t written = count; + uint32_t *dest; + const uint32_t *src; + size_t written; + size_t xfrsize; + size_t offset; + size_t page; + int i; 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. */ + offset = addr; if (addr >= STM32L4_FLASH_BASE) { - addr -= STM32L4_FLASH_BASE; + offset -= STM32L4_FLASH_BASE; } - if ((addr + count) > STM32L4_FLASH_SIZE) + if (offset + buflen > STM32L4_FLASH_SIZE) { return -EFAULT; } + /* Get the page number corresponding to the flash offset and the byte + * offset into the page. Align write destination to page boundary. + */ + + page = FLASH_BYTE2PAGE((uint32_t)offset); + offset &= FLASH_PAGE_MASK; + + dest = (uint32_t *)((uint8_t *)addr - offset); + written = 0; + sem_lock(); /* Get flash ready and begin flashing. */ flash_unlock(); - modifyreg32(STM32L4_FLASH_CR, 0, FLASH_CR_PG); + /* Loop until all of the data has been written */ - for (addr += STM32L4_FLASH_BASE; count; count -= 8, word += 2, addr += 8) + while (buflen > 0) { - uint32_t second_word; + /* How much can we write into this page? */ - /* Write first word. */ + xfrsize = MIN((size_t)FLASH_PAGE_SIZE - offset, buflen); - putreg32(*word, addr); + /* Do we need to use the intermediate buffer? */ - /* 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) + if (offset == 0 && xfrsize == FLASH_PAGE_SIZE) { - up_waste(); + /* No, we can take the data directly from the user buffer */ + + src = (const uint32_t *)buf; + } + else + { + /* Yes, copy data into global page buffer */ + + if (offset > 0) + { + memcpy(g_page_buffer, dest, offset); + } + + memcpy((uint8_t *)g_page_buffer + offset, buf, xfrsize); + + if (offset + xfrsize < FLASH_PAGE_SIZE) + { + memcpy((uint8_t *)g_page_buffer + offset + xfrsize, + (const uint8_t *)dest + offset + xfrsize, + FLASH_PAGE_SIZE - offset - xfrsize); + } + + src = g_page_buffer; } - /* Verify */ + /* Erase the page. Unlike most flash chips, STM32L4 is unable to + * write back existing data read from page without erase. + */ - if (getreg32(STM32L4_FLASH_SR) & FLASH_SR_WRITE_PROTECTION_ERROR) + flash_erase(page); + + /* Write the page. Must be with double-words. */ + + modifyreg32(STM32L4_FLASH_CR, 0, FLASH_CR_PG); + + for (i = 0; i < FLASH_PAGE_WORDS; i += 2) { - ret = -EROFS; - goto out; + *dest++ = *src++; + *dest++ = *src++; + + while (getreg32(STM32L4_FLASH_SR) & FLASH_SR_BSY) + { + up_waste(); + } + + /* Verify */ + + if (getreg32(STM32L4_FLASH_SR) & FLASH_SR_WRITE_PROTECTION_ERROR) + { + modifyreg32(STM32L4_FLASH_CR, FLASH_CR_PG, 0); + ret = -EROFS; + goto out; + } + + if (getreg32(dest-1) != *(src-1) || getreg32(dest-2) != *(src-2)) + { + modifyreg32(STM32L4_FLASH_CR, FLASH_CR_PG, 0); + ret = -EIO; + goto out; + } } - if (getreg32(addr) != *word || getreg32((addr + 4)) != second_word) - { - ret = -EIO; - goto out; - } + modifyreg32(STM32L4_FLASH_CR, FLASH_CR_PG, 0); - if (count == 4) - { - break; - } + /* Adjust pointers and counts for the next time through the loop */ + + written += xfrsize; + addr += xfrsize; + dest = (uint32_t *)addr; + buf = (void *)((uintptr_t)buf + xfrsize); + buflen -= xfrsize; + page++; } 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). diff --git a/drivers/mtd/mtd_config.c b/drivers/mtd/mtd_config.c index 32a2a72e593..89c4a5bccd0 100644 --- a/drivers/mtd/mtd_config.c +++ b/drivers/mtd/mtd_config.c @@ -1,7 +1,7 @@ /**************************************************************************** * drivers/mtd/mtd_config.c * - * Copyright (C) 2014, 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2014, 2017-2018 Gregory Nutt. All rights reserved. * Copyright (C) 2013 Ken Pettit. All rights reserved. * Author: Ken Pettit * With Updates from Gregory Nutt @@ -153,8 +153,8 @@ static const struct file_operations mtdconfig_fops = * ****************************************************************************/ -static int mtdconfig_readbytes(FAR struct mtdconfig_struct_s *dev, int offset, - FAR uint8_t *pdata, int readlen) +static int mtdconfig_readbytes(FAR struct mtdconfig_struct_s *dev, int offset, + FAR uint8_t *pdata, int readlen) { off_t bytestoread = readlen; off_t bytesthisblock, firstbyte; @@ -250,8 +250,8 @@ errout: * ****************************************************************************/ -static int mtdconfig_writebytes(FAR struct mtdconfig_struct_s *dev, int offset, - FAR const uint8_t *pdata, int writelen) +static int mtdconfig_writebytes(FAR struct mtdconfig_struct_s *dev, int offset, + FAR const uint8_t *pdata, int writelen) { int ret = OK; @@ -286,17 +286,6 @@ static int mtdconfig_writebytes(FAR struct mtdconfig_struct_s *dev, int offset, goto errout; } - /* Now erase the block */ - - ret = MTD_ERASE(dev->mtd, block, 1); - if (ret < 0) - { - /* Error erasing the block */ - - ret = -EIO; - goto errout; - } - index = offset - block * dev->blocksize; bytes_this_block = dev->blocksize - index; if (bytes_this_block > writelen) @@ -563,11 +552,11 @@ read_next: * block is available in the partition. * * Returned Value: - * offset to the next available entry (after consolidation).. + * Offset to the next available entry (after consolidation). * ****************************************************************************/ -static off_t mtdconfig_ramconsolidate(FAR struct mtdconfig_struct_s *dev) +static off_t mtdconfig_ramconsolidate(FAR struct mtdconfig_struct_s *dev) { FAR uint8_t *pBuf; FAR struct mtdconfig_header_s *phdr; @@ -601,7 +590,6 @@ static off_t mtdconfig_ramconsolidate(FAR struct mtdconfig_struct_s *dev) { /* Error doing block read */ - dst_offset = 0; goto errout; } @@ -612,7 +600,6 @@ static off_t mtdconfig_ramconsolidate(FAR struct mtdconfig_struct_s *dev) { /* Error erasing the block */ - dst_offset = 0; goto errout; } @@ -623,17 +610,25 @@ static off_t mtdconfig_ramconsolidate(FAR struct mtdconfig_struct_s *dev) sig[0] = 'C'; sig[1] = 'D'; sig[2] = CONFIGDATA_FORMAT_VERSION; - mtdconfig_writebytes(dev, 0, sig, sizeof(sig)); + + ret = mtdconfig_writebytes(dev, 0, sig, sizeof(sig)); + if (ret != sizeof(sig)) + { + /* Cannot write even the signature. */ + + ret = -EIO; + goto errout; + } } - /* Copy active items back to the MTD device */ + /* Copy active items back to the MTD device. */ while (src_offset < dev->erasesize) { phdr = (FAR struct mtdconfig_header_s *) &pBuf[src_offset]; if (phdr->id == MTD_ERASED_ID) { - /* No more data in this erase block */ + /* No more data in this erase block. */ src_offset = dev->erasesize; continue; @@ -650,7 +645,7 @@ static off_t mtdconfig_ramconsolidate(FAR struct mtdconfig_struct_s *dev) if (bytes_left_in_block < sizeof(*phdr) + phdr->len) { /* Item won't fit in the destination block. Move to - * the next block + * the next block. */ dst_block++; @@ -663,19 +658,29 @@ static off_t mtdconfig_ramconsolidate(FAR struct mtdconfig_struct_s *dev) DEBUGASSERT(dst_block != dev->neraseblocks); } - /* Now Write the item to the current dst_offset location */ + /* Now write the item to the current dst_offset location. */ ret = mtdconfig_writebytes(dev, dst_offset, (uint8_t *) phdr, sizeof(hdr)); - if (ret < 0) + if (ret != sizeof(hdr)) { - dst_offset = 0; + /* I/O Error! */ + + ret = -EIO; goto errout; } dst_offset += sizeof(hdr); - ret = mtdconfig_writebytes(dev, dst_offset, &pBuf[src_offset - + sizeof(hdr)], phdr->len); + ret = mtdconfig_writebytes(dev, dst_offset, + &pBuf[src_offset + sizeof(hdr)], phdr->len); + if (ret != phdr->len) + { + /* I/O Error! */ + + ret = -EIO; + goto errout; + } + dst_offset += phdr->len; /* Test if enough space in dst block for another header */ @@ -705,9 +710,13 @@ static off_t mtdconfig_ramconsolidate(FAR struct mtdconfig_struct_s *dev) src_offset = CONFIGDATA_BLOCK_HDR_SIZE; } -errout: kmm_free(pBuf); return dst_offset; + +errout: + kmm_free(pBuf); + ferr("ERROR: fail ram consolidate: %d\n", ret); + return 0; } /**************************************************************************** @@ -720,7 +729,7 @@ errout: * partition as it goes. * * Returned Value: - * offset to the next available entry (after consolidation).. + * Offset to the next available entry (after consolidation). * ****************************************************************************/ @@ -781,9 +790,17 @@ static off_t mtdconfig_consolidate(FAR struct mtdconfig_struct_s *dev) sig[0] = 'C'; sig[1] = 'D'; sig[2] = CONFIGDATA_FORMAT_VERSION; - mtdconfig_writebytes(dev, 0, sig, sizeof(sig)); - /* Now consolidate entries */ + ret = mtdconfig_writebytes(dev, 0, sig, sizeof(sig)); + if (ret != sizeof(sig)) + { + /* Cannot write even the signature. */ + + ret = -EIO; + goto errout; + } + + /* Now consolidate entries. */ src_block = 1; dst_block = 0; @@ -800,6 +817,7 @@ retry_relocate: { /* I/O Error! */ + ret = -EIO; goto errout; } @@ -840,7 +858,15 @@ retry_relocate: /* Copy this entry to the destination */ - mtdconfig_writebytes(dev, dst_offset, (uint8_t *) &hdr, sizeof(hdr)); + ret = mtdconfig_writebytes(dev, dst_offset, (uint8_t *) &hdr, sizeof(hdr)); + if (ret != sizeof(hdr)) + { + /* I/O Error! */ + + ret = -EIO; + goto errout; + } + src_offset += sizeof(hdr); dst_offset += sizeof(hdr); @@ -856,8 +882,23 @@ retry_relocate: /* Move the data. */ - mtdconfig_readbytes(dev, src_offset, pBuf, bytes); - mtdconfig_writebytes(dev, dst_offset, pBuf, bytes); + ret = mtdconfig_readbytes(dev, src_offset, pBuf, bytes); + if (ret != OK) + { + /* I/O Error! */ + + ret = -EIO; + goto errout; + } + + ret = mtdconfig_writebytes(dev, dst_offset, pBuf, bytes); + if (ret != bytes) + { + /* I/O Error! */ + + ret = -EIO; + goto errout; + } /* Update control variables */ @@ -916,10 +957,15 @@ retry_relocate: } } + kmm_free(pBuf); + return dst_offset; + errout: kmm_free(pBuf); + ferr("ERROR: fail consolidate: %d\n", ret); return 0; } + #endif /* CONFIG_MTD_CONFIG_RAM_CONSOLIDATE */ /**************************************************************************** @@ -1108,7 +1154,15 @@ retry: sig[0] = 'C'; sig[1] = 'D'; sig[2] = CONFIGDATA_FORMAT_VERSION; - mtdconfig_writebytes(dev, 0, sig, sizeof(sig)); + + ret = mtdconfig_writebytes(dev, 0, sig, sizeof(sig)); + if (ret != sizeof(sig)) + { + /* Cannot write even the signature. */ + + ret = -EIO; + goto errout; + } /* Now go try to read the signature again (as verification) */ @@ -1147,6 +1201,7 @@ retry: /* Now find a new entry for this config data */ retrycount = 0; + retry_find: offset = mtdconfig_findfirstentry(dev, &hdr); if (offset > 0 && hdr.id == MTD_ERASED_ID) @@ -1226,7 +1281,7 @@ retry_find: hdr.flags = MTD_ERASED_FLAGS; ret = mtdconfig_writebytes(dev, offset, (uint8_t *)&hdr, sizeof(hdr)); - if (ret < 0) + if (ret != sizeof(hdr)) { /* Cannot write even header! */