/**************************************************************************//** * Get access to eNVM controller for eNVM0 and eNVM1 */ static nvm_status_t get_ctrl_access(uint32_t nvm_offset, uint32_t length) { nvm_status_t access_req_status; /* * Gain access to eNVM controller(s). */ if(nvm_offset < NVM1_BOTTOM_OFFSET) { access_req_status = request_nvm_access(NVM_BLOCK_0); if(NVM_SUCCESS == access_req_status) { uint32_t last_offset; last_offset = nvm_offset + (length - 0x1u); if(last_offset >= NVM1_BOTTOM_OFFSET) { access_req_status = request_nvm_access(NVM_BLOCK_1); if(NVM_IN_USE_BY_OTHER_MASTER == access_req_status) { release_ctrl_access(); } } } } else { access_req_status = request_nvm_access(NVM_BLOCK_1); } return access_req_status; }
uint32_t NVM_read_page_write_count ( uint32_t addr ) { uint32_t write_count = 0; uint32_t block; uint32_t offset; uint32_t status; if((addr >= NVM_BASE_ADDRESS) && (addr < (NVM_BASE_ADDRESS + NVM_RSV_PROTECTION_OFFSET))) { offset = addr & NVM_OFFSET_SIGNIFICANT_BITS; /* Gain exclusive access to eNVM controller */ status = get_ctrl_access(offset, BYTES_PER_PAGE); /* Read page write counter. */ if(NVM_SUCCESS == status) { if(offset < NVM1_BOTTOM_OFFSET) { block = NVM_BLOCK_0; } else { block = NVM_BLOCK_1; offset = offset - NVM1_BOTTOM_OFFSET; } g_nvm[block]->NV_PAGE_STATUS |= 0x2u; if(block == NVM_BLOCK_0) { write_count = *((uint32_t *)((NVM0_BASE_ADDRESS + offset) & PAGE_ADDR_MASK)); } else { write_count = *((uint32_t *)((NVM1_BASE_ADDRESS + offset) & PAGE_ADDR_MASK)); } /* Wait for NVM to become ready. */ status = wait_nvm_ready(block); g_nvm[block]->NV_PAGE_STATUS &= ~(0x2u); } /* Release eNVM controllers so that other masters can gain access to it. */ release_ctrl_access(); /* The write count is contained in bits [24:4] of the page's auxiliary data. */ write_count = (write_count & AUX_DATA_WC_MASK) >> AUX_DATA_WC_SHIFT; }
/**************************************************************************//** * See mss_nvm.h for details of how to use this function. */ nvm_status_t NVM_unlock ( uint32_t start_addr, uint32_t length ) { nvm_status_t status; uint32_t nvm_offset; uint32_t first_page; uint32_t last_page; uint32_t current_page; uint32_t current_offset; uint32_t initial_nvm_config; /* * SAR 57547: Set the FREQRNG field of the eNVM configuration register * to its maximum value (i.e. 15) to ensure successful writes to eNVM. * Store the value of the eNVM configuration before updating it, so * that the prior configuration can be restored when the eNVM write * operation has completed. */ initial_nvm_config = SYSREG->ENVM_CR; SYSREG->ENVM_CR = (initial_nvm_config & NVM_FREQRNG_MASK) | NVM_FREQRNG_MAX; /* Check input parameters */ if((start_addr >= (NVM_BASE_ADDRESS + NVM_RSV_PROTECTION_OFFSET)) || \ ((start_addr >= NVM_RSV_PROTECTION_OFFSET) && \ (start_addr < NVM_BASE_ADDRESS)) || \ (!length) || \ ((start_addr + length) >= (NVM_BASE_ADDRESS + NVM_RSV_PROTECTION_OFFSET))|| \ (((start_addr + length) >= NVM_RSV_PROTECTION_OFFSET) && \ ((start_addr + length) < NVM_BASE_ADDRESS))) { if(((start_addr >= (NVM_BASE_ADDRESS + NVM_RSV_PROTECTION_OFFSET)) && \ (start_addr <= (NVM_BASE_ADDRESS + NVM_TOP_OFFSET))) || \ ((start_addr >= NVM_RSV_PROTECTION_OFFSET) && (start_addr <= NVM_TOP_OFFSET)) || \ (((start_addr + length) >= (NVM_BASE_ADDRESS + NVM_RSV_PROTECTION_OFFSET)) && \ ((start_addr + length) <= (NVM_BASE_ADDRESS + NVM_TOP_OFFSET))) || \ (((start_addr + length) >= NVM_RSV_PROTECTION_OFFSET) && \ ((start_addr + length) <= NVM_TOP_OFFSET))) { status = NVM_PROTECTION_ERROR; } else { status = NVM_INVALID_PARAMETER; } } else { /* Ignore upper address bits to ignore remapping setting. */ nvm_offset = start_addr & NVM_OFFSET_SIGNIFICANT_BITS; /* Ignore remapping. */ /* Check against attempt to write data larger than eNVM. */ ASSERT((nvm_offset + length) <= MAX_504K_OFFSET); if((nvm_offset + length) <= MAX_504K_OFFSET) { first_page = nvm_offset / BYTES_PER_PAGE; last_page = (nvm_offset + (length - 1u)) / BYTES_PER_PAGE; /* Gain exclusive access to eNVM controller */ status = get_ctrl_access(nvm_offset, length); /* Unlock eNVM one page at a time. */ if(NVM_SUCCESS == status) { uint32_t block; uint32_t inc; uint32_t * p_nvm32; uint32_t errors_and_warnings; for(current_page = first_page; (current_page <= last_page) && ((NVM_SUCCESS == status) ||(NVM_WRITE_THRESHOLD_WARNING == status)); ++current_page) { uint32_t ctrl_status; if(current_page > PAGES_PER_BLOCK) { block = NVM_BLOCK_1; } else { block = NVM_BLOCK_0; } if(g_nvm[block]->STATUS & MSS_NVM_WR_DENIED) { /* Clear the access denied flag */ g_nvm[block]->CLRHINT |= ACCESS_DENIED_FLAG_CLEAR; } current_offset = (current_page << 0x7u); p_nvm32 = (uint32_t *)(NVM_BASE_ADDRESS + current_offset); for(inc = 0u; inc < WD_WORD_SIZE; ++inc) { g_nvm32[block]->WD[inc] = p_nvm32[inc]; } g_nvm[block]->PAGE_LOCK = NVM_DO_NOT_LOCK_PAGE; g_nvm[block]->CMD = USER_UNLOCK | (current_offset & PAGE_ADDR_MASK); /* Issue program command */ g_nvm[block]->CMD = PROG_ADS | (current_offset & PAGE_ADDR_MASK); /* Wait for NVM to become ready. */ ctrl_status = wait_nvm_ready(block); /* Check for errors and warnings. */ errors_and_warnings = ctrl_status & (WRITE_ERROR_MASK | MSS_NVM_WRCNT_OVER); if(errors_and_warnings) { uint32_t nvm_hw_status; nvm_hw_status = g_nvm[block]->STATUS; status = get_error_code(nvm_hw_status); } } /* Release eNVM controllers so that other masters can gain access to it. */ release_ctrl_access(); } } else { status = NVM_INVALID_PARAMETER; } } /* Restore back to original value. */ SYSREG->ENVM_CR = initial_nvm_config; return status; }
/**************************************************************************//** * See mss_nvm.h for details of how to use this function. */ nvm_status_t NVM_write ( uint32_t start_addr, const uint8_t * pidata, uint32_t length, uint32_t lock_page ) { nvm_status_t status; uint32_t nvm_offset; uint32_t device_version; uint32_t initial_nvm_config; /* * SAR 57547: Set the FREQRNG field of the eNVM configuration register * to its maximum value (i.e. 15) to ensure successful writes to eNVM. * Store the value of the eNVM configuration before updating it, so * that the prior configuration can be restored when the eNVM write * operation has completed. */ initial_nvm_config = SYSREG->ENVM_CR; SYSREG->ENVM_CR = (initial_nvm_config & NVM_FREQRNG_MASK) | NVM_FREQRNG_MAX; /* Check input parameters */ if((start_addr >= (NVM_BASE_ADDRESS + NVM_RSV_PROTECTION_OFFSET)) || \ ((start_addr >= NVM_RSV_PROTECTION_OFFSET) && \ (start_addr < NVM_BASE_ADDRESS)) || \ (!pidata) || \ (!length) || \ ((start_addr + length) >= (NVM_BASE_ADDRESS + NVM_RSV_PROTECTION_OFFSET))|| \ (((start_addr + length) >= NVM_RSV_PROTECTION_OFFSET) && \ ((start_addr + length) < NVM_BASE_ADDRESS))|| \ (lock_page > PARAM_LOCK_PAGE_FLAG)) { if(((start_addr >= (NVM_BASE_ADDRESS + NVM_RSV_PROTECTION_OFFSET)) && \ (start_addr <= (NVM_BASE_ADDRESS + NVM_TOP_OFFSET))) || \ ((start_addr >= NVM_RSV_PROTECTION_OFFSET) && (start_addr <= NVM_TOP_OFFSET)) || \ (((start_addr + length) >= (NVM_BASE_ADDRESS + NVM_RSV_PROTECTION_OFFSET)) && \ ((start_addr + length) <= (NVM_BASE_ADDRESS + NVM_TOP_OFFSET))) || \ (((start_addr + length) >= NVM_RSV_PROTECTION_OFFSET) && \ ((start_addr + length) <= NVM_TOP_OFFSET))) { status = NVM_PROTECTION_ERROR; } else { status = NVM_INVALID_PARAMETER; } } else { /* * Prevent pages being locked for silicon versions which do not allow * locked pages to be unlocked. */ device_version = SYSREG->DEVICE_VERSION; if((0x0000F802u == device_version) || (0x0001F802u == device_version)) { lock_page = NVM_DO_NOT_LOCK_PAGE; } /* Ignore upper address bits to ignore remapping setting. */ nvm_offset = start_addr & NVM_OFFSET_SIGNIFICANT_BITS; /* Ignore remapping. */ /* Check against attempt to write data larger than eNVM. */ ASSERT((nvm_offset + length) <= MAX_504K_OFFSET); if((nvm_offset + length) <= MAX_504K_OFFSET) { /* Gain exclusive access to eNVM controller */ status = get_ctrl_access(nvm_offset, length); /* Write eNVM one page at a time. */ if(NVM_SUCCESS == status) { uint32_t remaining_length = length; uint32_t errors_and_warnings; while(remaining_length > 0u) { uint32_t length_written; uint32_t nvm_hw_status = 0u; length_written = write_nvm(start_addr + (length - remaining_length), &pidata[length - remaining_length], remaining_length, lock_page, &nvm_hw_status); /* Check for errors and warnings. */ errors_and_warnings = nvm_hw_status & (WRITE_ERROR_MASK | MSS_NVM_WRCNT_OVER); if(errors_and_warnings) { /* * Ensure that the status returned by the NVM_write() * function is NVM_WRITE_THRESHOLD_WARNING if at least one * of the written eNVM pages indicate a write over * threshold condition. */ status = get_error_code(nvm_hw_status); } if((NVM_SUCCESS == status) || (NVM_WRITE_THRESHOLD_WARNING == status )) { if(remaining_length > length_written) { remaining_length -= length_written; } else { remaining_length = 0u; } } else { remaining_length = 0u; } } /* Release eNVM controllers so that other masters can gain access to it. */ release_ctrl_access(); } } else { status = NVM_INVALID_PARAMETER; } } /* Restore back to original value. */ SYSREG->ENVM_CR = initial_nvm_config; return status; }
/**************************************************************************//** * See mss_nvm.h for details of how to use this function. */ nvm_status_t NVM_unlock ( uint32_t start_addr, uint32_t length ) { nvm_status_t status; uint32_t nvm_offset; uint32_t first_page; uint32_t last_page; uint32_t current_page; uint32_t current_offset; /* Ignore upper address bits to ignore remapping setting. */ nvm_offset = start_addr & NVM_OFFSET_SIGNIFICANT_BITS; /* Ignore remapping. */ /* Check against attempt to write data larger than eNVM. */ ASSERT((nvm_offset + length) < MAX_512K_OFFSET); if((nvm_offset + length) < MAX_512K_OFFSET) { current_offset = nvm_offset; first_page = nvm_offset / BYTES_PER_PAGE; last_page = (nvm_offset + length) / BYTES_PER_PAGE; /* Gain exclusive access to eNVM controller */ status = get_ctrl_access(nvm_offset, length); /* Unlock eNVM one page at a time. */ if(NVM_SUCCESS == status) { uint32_t block; uint32_t inc; uint32_t first_word; uint32_t word_offset; uint32_t * p_nvm32; uint32_t errors; p_nvm32 = (uint32_t *)NVM_BASE_ADDRESS; first_word = nvm_offset / 4u; word_offset = first_word; for(current_page = first_page; (current_page <= last_page) && (NVM_SUCCESS == status); ++current_page) { uint32_t ctrl_status; if(word_offset >= BLOCK1_FIRST_WORD_OFFSET) { block = NVM_BLOCK_1; } else { block = NVM_BLOCK_0; } for(inc = 0u; inc < WD_WORD_SIZE; ++inc) { g_nvm32[block]->WD[inc] = p_nvm32[word_offset]; ++word_offset; } g_nvm[block]->PAGE_LOCK = NVM_DO_NOT_LOCK_PAGE; g_nvm[block]->CMD = USER_UNLOCK; /* Issue program command */ g_nvm[block]->CMD = PROG_ADS | (current_offset & PAGE_ADDR_MASK); current_offset += BYTES_PER_PAGE; /* Wait for NVM to become ready. */ ctrl_status = wait_nvm_ready(block); /* Check for errors. */ errors = ctrl_status & WRITE_ERROR_MASK; if(errors) { uint32_t nvm_hw_status; nvm_hw_status = g_nvm[block]->STATUS; status = get_error_code(nvm_hw_status); } } /* Release eNVM controllers so that other masters can gain access to it. */ release_ctrl_access(); } } else { status = NVM_INVALID_PARAMETER; } return status; }
/**************************************************************************//** * See mss_nvm.h for details of how to use this function. */ nvm_status_t NVM_write ( uint32_t start_addr, const uint8_t * pidata, uint32_t length, uint32_t lock_page ) { nvm_status_t status; uint32_t nvm_offset; uint32_t device_version; /* * Prevent pages being locked for silicon versions which do not allow * locked pages to be unlocked. */ device_version = SYSREG->DEVICE_VERSION; if((0x0000F802u == device_version) || (0x0001F802u == device_version)) { lock_page = NVM_DO_NOT_LOCK_PAGE; } /* Ignore upper address bits to ignore remapping setting. */ nvm_offset = start_addr & NVM_OFFSET_SIGNIFICANT_BITS; /* Ignore remapping. */ /* Check against attempt to write data larger than eNVM. */ ASSERT((nvm_offset + length) < MAX_512K_OFFSET); if((nvm_offset + length) < MAX_512K_OFFSET) { /* Gain exclusive access to eNVM controller */ status = get_ctrl_access(nvm_offset, length); /* Write eNVM one page at a time. */ if(NVM_SUCCESS == status) { uint32_t remaining_length = length; while((remaining_length > 0u) && (NVM_SUCCESS == status)) { uint32_t length_written; uint32_t nvm_hw_status = 0u; length_written = write_nvm(start_addr + (length - remaining_length), &pidata[length - remaining_length], remaining_length, lock_page, &nvm_hw_status); if(0u == length_written) { status = get_error_code(nvm_hw_status); } else if(remaining_length > length_written) { remaining_length -= length_written; } else { remaining_length = 0u; } } /* Release eNVM controllers so that other masters can gain access to it. */ release_ctrl_access(); } } else { status = NVM_INVALID_PARAMETER; } return status; }