| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- /*
- * Copyright (C) 2013 Uwe Bonnes
- *
- * 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 of the copyright holders nor the names of
- * 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.
- *
- * For additional information see http://www.ethernut.de/
- *
- */
- #include <stdint.h>
- #include <string.h>
- #include <cfg/arch.h>
- #include <arch/cm3.h>
- #include <dev/iap_flash.h>
- #if defined(MCU_STM32L1)
- #include <arch/cm3/stm/vendor/stm32l1xx.h>
- #else
- #warning "STM32 family has no L1 compatible FLASH/EEPROM"
- #endif
- /*Sectors are the unit for write protection, pages for erase */
- #define FLASH_SECTOR_SIZE (1<<12)
- #define FLASH_SECTOR_MASK 0xfffff000
- #define FLASH_SECTOR_SHIFT 12
- #define FLASH_PAGE_SIZE (1<<8)
- #define FLASH_PAGE_MASK 0xffffff00
- #define FLASH_PAGE_SHIFT 8
- #define ERASED_PATTERN_32 0
- #if defined (STM32L1XX_MDP)
- #define FLASH_SIZE_REG 0x1ff800cc
- static uint32_t pagelist[32] = { 0 };
- #elif defined (STM32L1XX_HD)
- #define FLASH_SIZE_REG 0x1ff800cc
- static uint32_t pagelist[48] = { 0 };
- #elif defined (STM32L1XX_XL)
- #define FLASH_SIZE_REG 0x1ff800cc
- static uint32_t pagelist[64] = { 0 };
- #else
- #define FLASH_SIZE_REG 0x1ff8004c
- static uint32_t pagelist[16] = { 0 };
- #endif
- uint8_t pagelist_init_done = 0;
- volatile uint32_t *pagelist_bb;
- #define FLASH_PEKEY1 0x89abcdef
- #define FLASH_PEKEY2 0x02030405
- #define FLASH_PRGKEY1 0x8c9daebf
- #define FLASH_PRGKEY2 0x13141516
- #define FLASH_OPTKEY1 0xfbead9c8
- #define FLASH_OPTKEY2 0x24252627
- void FlashUntouch(void)
- {
- memset(pagelist, 0, sizeof(pagelist));
- pagelist_init_done = 1;
- pagelist_bb = CM3BB_BASE(pagelist);
- }
- uint32_t IapFlashEnd(void)
- {
- uint16_t size;
- size = *(__I uint16_t *) FLASH_SIZE_REG;
- uint32_t retval = FLASH_BASE - 1;
- #if defined (STM32L1XX_MDP) || defined (STM32L1XX_HD) || defined (STM32L1XX_)
- if (((*(__I uint32_t *)0xe0042000) & 0xfff) == 0x436) {
- if (size == 0)
- retval += 384 * 1024;
- else
- retval += 256 * 1024;
- }
- #endif
- retval += size * 1024;
- return retval;
- }
- /*!
- * \brief Unlocks the FLASH Program Erase Controller.
- * \retval 0 on success, FLASH_LOCKED else.
- */
- static FLASH_Status FLASH_Unlock( void )
- {
- FLASH->PEKEYR = FLASH_PEKEY1;
- FLASH->PEKEYR = FLASH_PEKEY2;
- if (FLASH->PECR & FLASH_PECR_PELOCK)
- return FLASH_LOCKED;
- FLASH->PRGKEYR = FLASH_PRGKEY1;
- FLASH->PRGKEYR = FLASH_PRGKEY2;
- return (FLASH->PECR & FLASH_PECR_PRGLOCK)?FLASH_LOCKED:FLASH_COMPLETE;
- }
- /*!
- * \brief Waits for a Flash operation to complete or a TIMEOUT to occur.
- * \param Timeout: FLASH progamming Timeout in Microseconds
- *
- * \retval FLASH Status: FLASH_COMPLETE or appropriate error.
- */
- static FLASH_Status FLASH_GetStatus(void)
- {
- FLASH_Status rs = FLASH_COMPLETE;
- /* Decode the Flash Status
- * Check BSY last, so maybe it has completed meanwhile*/
- if (FLASH->SR & (FLASH_SR_SIZERR |FLASH_SR_PGAERR))
- rs = FLASH_ERROR_PG;
- else if (FLASH->SR & FLASH_SR_WRPERR)
- rs = FLASH_ERROR_WRP;
- else if (FLASH->SR & FLASH_SR_BSY)
- rs = FLASH_BUSY;
- /* Return the Flash Status */
- return rs;
- }
- /*!
- * \brief Waits for a Flash operation to complete
- *
- * \retval FLASH Status: FLASH_COMPLETE or appropriate error.
- * Every flash access stalls while FLASH erase/program is running
- * The erase/program process however will finish at some point
- * and may indicate failure then
- */
- static FLASH_Status FlashWaitReady(void)
- {
- FLASH_Status status;
- do
- status = FLASH_GetStatus();
- while(status == FLASH_BUSY);
- /* Return the operation status */
- return status;
- }
- /*!
- * \brief Erase specified FLASH sector. Use "Program memory page erase"
- *
- * \param sector Sector to erase.
- *
- * \return FLASH Status: FLASH_COMPLETE or appropriate error.
- */
- static FLASH_Status FlashErasePage(void *address_in_page)
- {
- FLASH_Status rs = FLASH_COMPLETE;
- uint32_t first_address_in_page = (uint32_t)address_in_page & FLASH_PAGE_MASK;
- uint32_t *page = (uint32_t*)first_address_in_page;
- uint32_t current_page = ((uint32_t)address_in_page - FLASH_BASE) >> FLASH_PAGE_SHIFT;
- int i;
- /* Check, if page really needs erase */
- for(i = 0; i < FLASH_PAGE_SIZE >> 2; i++) {
- if (page[i] != ERASED_PATTERN_32)
- break;
- }
- if ( i >= FLASH_PAGE_SIZE >> 2)
- rs = FLASH_COMPLETE;
- else {
- /* Wait for last operation to be completed */
- rs = FlashWaitReady();
- if(rs == FLASH_COMPLETE) {
- FLASH->PECR = FLASH_PECR_ERASE;
- FLASH->PECR = FLASH_PECR_ERASE | FLASH_PECR_PROG;
- rs = FlashWaitReady();
- if (rs != FLASH_COMPLETE)
- return rs;
- *(uint32_t*) first_address_in_page = 0;
- rs = FlashWaitReady();
- FLASH->PECR = 0;
- }
- }
- if (rs == FLASH_COMPLETE )
- pagelist_bb[current_page] = 1;
- /* Return the Erase Status */
- return rs;
- }
- /*!
- * \brief Program any data to FLASH.
- *
- * It always erases the page, eventually keeping old content
- *
- * \param dst Pointer to address anywhere in FLASH.
- * \param src Pointer to source data. With SRC == NULL, the region
- * is checked for write protection
- * \param len Number of bytes to be written/checked.
- * \param mode Erase mode (Always, on first access to block, never).
- *
- * \return FLASH Status: FLASH_COMPLETE or appropriate error.
- */
- FLASH_Status IapFlashWrite( void* dst, void* src, size_t len,
- FLASH_ERASE_MODE mode)
- {
- FLASH_Status rs = FLASH_COMPLETE;
- uint32_t sector_start, sector_end;
- int i;
- void *wptr = dst;
- void *rptr = src;
- uint32_t length = len;
- volatile uint32_t *wrpr_bb = CM3BBADDR(FLASH_R_BASE, FLASH_TypeDef, WRPR , 0);
- volatile uint32_t *wrpr1_bb = CM3BBADDR(FLASH_R_BASE, FLASH_TypeDef, WRPR1, 0);
- if (len == 0)
- return FLASH_COMPLETE;
- /* Check top boundary */
- if ((((uint32_t)dst - 1 + len) > IapFlashEnd()) ||
- ((uint32_t)dst < FLASH_BASE))
- {
- return FLASH_BOUNDARY;
- }
- if ((rs = FLASH_Unlock()) != FLASH_COMPLETE)
- {
- /* Unlocking failed for any reason */
- return FLASH_LOCKED;
- }
- /* Check for write protected sectors */
- sector_start = ((uint32_t)dst - FLASH_BASE) >> FLASH_SECTOR_SHIFT;
- sector_end = ((uint32_t)dst + len - FLASH_BASE) >> FLASH_SECTOR_SHIFT;
- for (i = sector_start; i < sector_end; i++) {
- if (i < 32) {
- if (wrpr_bb[i]) {
- rs = FLASH_ERROR_WRP;
- goto done;
- }
- }
- else {
- if (wrpr1_bb[i - 32]) {
- rs = FLASH_ERROR_WRP;
- goto done;
- }
- }
- }
- if (src == NULL) {
- /* Only a check for write protection was requested */
- rs = FLASH_COMPLETE;
- goto done;
- }
- if (pagelist_init_done == 0)
- FlashUntouch();
- while( (length) && (rs==FLASH_COMPLETE))
- {
- uint32_t current_page = ((uint32_t)wptr - FLASH_BASE) >> FLASH_PAGE_SHIFT;
- uint32_t current_length = length;
- uint32_t offset_in_page = (uint32_t)wptr & ~FLASH_PAGE_MASK;
- uint8_t page_buffer[FLASH_PAGE_SIZE];
- if (offset_in_page + current_length > FLASH_PAGE_SIZE)
- current_length = FLASH_PAGE_SIZE - offset_in_page;
- if ((offset_in_page == 0) && (current_length == FLASH_PAGE_SIZE)) {
- /* Handle full page writes */
- volatile uint32_t *cwptr = (uint32_t*) wptr;
- volatile uint32_t *crptr = (uint32_t*) rptr;
- /* Check if content really changed */
- rs = memcmp(page_buffer, (void*)((uint32_t)wptr & FLASH_PAGE_MASK), FLASH_PAGE_SIZE);
- if (rs == 0)
- continue;
- rs = FlashErasePage(wptr);
- if (rs != FLASH_COMPLETE)
- goto done;
- for (i = 0; i < FLASH_PAGE_SIZE >> 2; i++)
- cwptr[i] = crptr[i];
- rs = FlashWaitReady();
- if(rs != FLASH_COMPLETE)
- goto done;
- wptr += current_length;
- rptr += current_length;
- length -= current_length;
- continue;
- }
- else { /* Handle partial page writes */
- uint8_t page_buffer[FLASH_PAGE_SIZE];
- int do_page_erase;
- do_page_erase = ((mode == FLASH_ERASE_ALWAYS) ||
- ((mode != FLASH_ERASE_NEVER) &&
- (pagelist_bb[current_page] == 0)));
- if (do_page_erase)
- memset(page_buffer, (uint8_t) ERASED_PATTERN_32, FLASH_PAGE_SIZE);
- else {
- rs = memcmp(rptr, wptr, current_length);
- if (rs == 0)
- goto chunk_done;
- if ((offset_in_page & 3 ) || (current_length & 3)) {
- memcpy(page_buffer,
- (void*)((uint32_t)wptr & FLASH_PAGE_MASK),
- FLASH_PAGE_SIZE);
- }
- else
- {
- volatile uint32_t *cwptr = (uint32_t*)wptr;
- volatile uint32_t *crptr = (uint32_t*)rptr;
- int n_words = current_length >> 2;
- for (i = 0; i < n_words; i++) {
- /* We can only write dwords with all bit flashed
- * or the flash erased!*/
- if ((crptr[i] != ~ERASED_PATTERN_32) ||
- (cwptr[i] != ERASED_PATTERN_32))
- break;
- }
- if ( i < n_words) {
- /* Copy old content for pattern reason */
- memcpy(page_buffer,
- (void*)((uint32_t)wptr & FLASH_PAGE_MASK),
- FLASH_PAGE_SIZE);
- }
- else {
- for (i = 0; i < n_words; i++)
- if (cwptr[i] != crptr[i])
- cwptr[i] = crptr[i];
- rs = FlashWaitReady();
- if(rs != FLASH_COMPLETE)
- goto done;
- goto chunk_done;
- }
- }
- }
- memcpy(page_buffer + offset_in_page, rptr, current_length);
- rs = FlashErasePage(wptr);
- if (rs != FLASH_COMPLETE)
- goto done;
- /* Program the sector */
- rs = FlashWaitReady();
- if (rs != FLASH_COMPLETE)
- goto done;
- else
- {
- int i;
- volatile uint32_t *cwptr =
- (uint32_t*) ((uint32_t)wptr & FLASH_PAGE_MASK);
- volatile uint32_t *crptr = (uint32_t*) page_buffer;
- for (i=0; i < (FLASH_PAGE_SIZE >> 2); i++)
- if (cwptr[i] != crptr[i])
- cwptr[i] = crptr[i];
- rs = FlashWaitReady();
- if(rs != FLASH_COMPLETE)
- goto done;
- }
- chunk_done:
- wptr += current_length;
- rptr += current_length;
- length -= current_length;
- }
- }
- /* Check the written data */
- wptr = dst;
- rptr = src;
- length = len;
- /* Align flash access to 4 Byte Boundary*/
- while (length && ((uint32_t)wptr & FLASH_PAGE_MASK)) {
- if(*(volatile uint8_t*)wptr++ != *(uint8_t*)rptr++) {
- rs = FLASH_COMPARE;
- goto done;
- }
- length--;
- }
- /* Now compare 32-bit at a time*/
- while (length > 3) {
- if(*(volatile uint32_t*)wptr != *(uint32_t*)rptr) {
- rs = FLASH_COMPARE;
- goto done;
- }
- length -= 4;
- wptr += +4;
- rptr += +4;
- }
- while (length) {
- if((*(volatile uint8_t*)wptr++) != *(uint8_t*)rptr++) {
- rs = FLASH_COMPARE;
- goto done;
- }
- length--;
- }
- /* Lock the FLASH again */
- done:
- FLASH->PECR = FLASH_PECR_PELOCK;
- return rs;
- }
- /*!
- * \brief Try to protect/unprotect the requested flash region.
- *
- * \param dst Pointer to address anywhere in FLASH.
- * \param len Length of region in bytes.
- * \param ena 0 disables write protection anything else write-protects.
- *
- * \return FLASH_Status: FLASH_COMPLETE or appropriate error.
- */
- FLASH_Status IapFlashWriteProtect(void *dst, size_t len, int ena)
- {
- uint32_t sector_start, sector_end;
- int i;
- uint32_t optcr;
- FLASH_Status rs = FLASH_COMPLETE;
- uint32_t flash_end_addr = IapFlashEnd();
- uint8_t nwrpr;
- uint32_t cwrpr;
- uint32_t wrpr = FLASH->WRPR; /* Old write protection state */
- volatile uint32_t *wrpr_bb = CM3BB_BASE(&wrpr);
- #if defined (STM32L1XX_MDP) || defined (STM32L1XX_HD) || defined (STM32L1XX_XL)
- uint32_t wrpr1[3] = {FLASH->WRPR1, FLASH->WRPR2, FLASH->WRPR3} ; /* Old write protection state */
- volatile uint32_t *wrpr1_bb = CM3BB_BASE(wrpr1);
- #endif
- /* Check boundaries */
- if ((((uint32_t)dst + len) > flash_end_addr) || ((uint32_t)dst < FLASH_BASE))
- {
- return FLASH_BOUNDARY;
- }
- if (len == 0)
- return FLASH_COMPLETE;
- /* Wait for last operation to be completed */
- rs = FlashWaitReady();
- if(rs != FLASH_COMPLETE)
- return rs;
- FLASH->OPTKEYR = FLASH_OPTKEY1;
- FLASH->OPTKEYR = FLASH_OPTKEY2;
- optcr = FLASH->PECR;
- if (optcr & FLASH_PECR_OPTLOCK)
- return FLASH_ERROR_PG;
- sector_start = (uint32_t) dst & FLASH_PAGE_MASK;
- sector_end = ((uint32_t) dst +len -1) & FLASH_PAGE_MASK;
- if(ena)
- ena = 1;
- else
- ena = 0;
- for (i = sector_start; i <= sector_end; i++) {
- if (i < 32) {
- wrpr_bb[i] = ena;
- }
- #if defined (STM32L1XX_MDP) || defined (STM32L1XX_HD)|| defined (STM32L1XX_XL)
- else {
- wrpr1_bb[i - 32] = ena;
- }
- #endif
- }
- FLASH->PEKEYR = FLASH_PEKEY1;
- FLASH->PEKEYR = FLASH_PEKEY2;
- if (FLASH->PECR & FLASH_PECR_PELOCK)
- return FLASH_LOCKED;
- FLASH->OPTKEYR = FLASH_OPTKEY1;
- FLASH->OPTKEYR = FLASH_OPTKEY2;
- if (FLASH->PECR & FLASH_PECR_OPTLOCK) {
- FLASH->PECR |= FLASH_PECR_PELOCK;
- return FLASH_ERROR_PG;
- }
- if (sector_start < 16) {
- nwrpr = wrpr >> 8;
- cwrpr = ~nwrpr << 8 | nwrpr;
- nwrpr = wrpr ;
- cwrpr = cwrpr << 16 | ~nwrpr << 8 | nwrpr;
- OB->WRP01 = cwrpr;
- /* pm0062 tells to clear option byte errors */
- FLASH->SR = FLASH_SR_OPTVERRUSR | FLASH_SR_OPTVERR;
- }
- if ((sector_start >= 16) || (sector_end < 32)) {
- nwrpr = wrpr >> 24;
- cwrpr = ~nwrpr << 8 | nwrpr;
- nwrpr = wrpr >> 16;
- cwrpr = cwrpr << 16 | ~nwrpr << 8 | nwrpr;
- OB->WRP23 = cwrpr;
- FLASH->SR = FLASH_SR_OPTVERRUSR | FLASH_SR_OPTVERR;
- }
- #if defined (STM32L1XX_MDP) || defined (STM32L1XX_HD) || defined (STM32L1XX_XL)
- if ((sector_start >= 32) || (sector_end < 48)) {
- nwrpr = wrpr1[0] >> 8;
- cwrpr = ~nwrpr << 8 | nwrpr;
- nwrpr = wrpr1[0];
- cwrpr = cwrpr << 16 | ~nwrpr << 8 | nwrpr;
- OB->WRP45 = cwrpr;
- FLASH->SR = FLASH_SR_OPTVERRUSR | FLASH_SR_OPTVERR;
- }
- if ((sector_start >= 48) || (sector_end < 64)) {
- nwrpr = wrpr1[0] >> 24;
- cwrpr = ~nwrpr << 8 | nwrpr;
- nwrpr = wrpr1[0] >> 16;
- cwrpr = cwrpr << 16 | ~nwrpr << 8 | nwrpr;
- OB->WRP67 = cwrpr;
- FLASH->SR = FLASH_SR_OPTVERRUSR | FLASH_SR_OPTVERR;
- }
- #endif
- #if defined (STM32L1XX_HD) || defined (STM32L1XX_XL)
- if ((sector_start >= 64) || (sector_end < 80)) {
- nwrpr = wrpr1[1] >> 8;
- cwrpr = ~nwrpr << 8 | nwrpr;
- nwrpr = wrpr1[1];
- cwrpr = cwrpr << 16 | ~nwrpr << 8 | nwrpr;
- OB->WRP89 = cwrpr;
- FLASH->SR = FLASH_SR_OPTVERRUSR | FLASH_SR_OPTVERR;
- }
- if ((sector_start >= 80) || (sector_end < 96)) {
- nwrpr = wrpr1[1] >> 24;
- cwrpr = ~nwrpr << 8 | nwrpr;
- nwrpr = wrpr1[1] >> 16;
- cwrpr = cwrpr << 16 | ~nwrpr << 8 | nwrpr;
- OB->WRP1011 = cwrpr;
- FLASH->SR = FLASH_SR_OPTVERRUSR | FLASH_SR_OPTVERR;
- }
- #endif
- #if defined (STM32L1XX_XL)
- if ((sector_start >= 80) || (sector_end < 96)) {
- nwrpr = wrpr1[2] >> 8;
- cwrpr = ~nwrpr << 8 | nwrpr;
- nwrpr = wrpr1[2];
- cwrpr = cwrpr << 16 | ~nwrpr << 8 | nwrpr;
- OB->WRP89 = cwrpr;
- FLASH->SR = FLASH_SR_OPTVERRUSR | FLASH_SR_OPTVERR;
- }
- if ((sector_start >= 80) || (sector_end < 96)) {
- nwrpr = wrpr1[2] >> 24;
- cwrpr = ~nwrpr << 8 | nwrpr;
- nwrpr = wrpr1[2] >> 16;
- cwrpr = cwrpr << 16 | ~nwrpr << 8 | nwrpr;
- OB->WRP1011 = cwrpr;
- FLASH->SR = FLASH_SR_OPTVERRUSR | FLASH_SR_OPTVERR;
- }
- #endif
- /* Wait for last operation to be completed */
- rs = FlashWaitReady();
- FLASH->PECR |= FLASH_PECR_PELOCK;
- return rs;
- }
|