error_t flash_manager_init(const flash_intf_t *flash_intf) { error_t status; // Assert that interface has been properly uninitialized flash_manager_printf("flash_manager_init()\r\n"); if (state != STATE_CLOSED) { util_assert(0); return ERROR_INTERNAL; } // Check for a valid flash interface if (!flash_intf_valid(flash_intf)) { util_assert(0); return ERROR_INTERNAL; } // Initialize variables memset(buf, 0xFF, sizeof(buf)); buf_empty = true; current_sector_valid = false; current_write_block_addr = 0; current_write_block_size = 0; current_sector_addr = 0; current_sector_size = 0; last_addr = 0; intf = flash_intf; // Initialize flash status = intf->init(); flash_manager_printf(" intf->init ret=%i\r\n", status); if (ERROR_SUCCESS != status) { return status; } if (!page_erase_enabled) { // Erase flash and unint if there are errors status = intf->erase_chip(); flash_manager_printf(" intf->erase_chip ret=%i\r\n", status); if (ERROR_SUCCESS != status) { intf->uninit(); return status; } } state = STATE_OPEN; return status; }
error_t flash_manager_uninit(void) { error_t flash_uninit_error; error_t flash_write_error = ERROR_SUCCESS; flash_manager_printf("flash_manager_uninit()\r\n"); if (STATE_CLOSED == state) { util_assert(0); return ERROR_INTERNAL; } // Write out current page if ((STATE_OPEN == state) && (!buf_empty)) { flash_write_error = intf->program_page(current_write_block_addr, buf, current_write_block_size); flash_manager_printf(" intf->program_page(addr=0x%x, size=0x%x) ret=%i\r\n", current_write_block_addr, current_write_block_size, flash_write_error); } // Close flash interface (even if there was an error during program_page) flash_uninit_error = intf->uninit(); flash_manager_printf(" intf->uninit() ret=%i\r\n", flash_uninit_error); // Reset variables to catch accidental use memset(buf, 0xFF, sizeof(buf)); buf_empty = true; current_sector_valid = false; current_write_block_addr = 0; current_write_block_size = 0; current_sector_addr = 0; current_sector_size = 0; last_addr = 0; state = STATE_CLOSED; // Make sure an error from a page write or from an // uninit gets propagated if (flash_uninit_error != ERROR_SUCCESS) { return flash_uninit_error; } if (flash_write_error != ERROR_SUCCESS) { return flash_write_error; } return ERROR_SUCCESS; }
static error_t setup_next_sector(uint32_t addr) { uint32_t min_prog_size; uint32_t sector_size; error_t status; min_prog_size = intf->program_page_min_size(addr); sector_size = intf->erase_sector_size(addr); if ((min_prog_size <= 0) || (sector_size <= 0)) { // Either of these conditions could cause divide by 0 error util_assert(0); return ERROR_INTERNAL; } // Assert required size and alignment util_assert(sizeof(buf) >= min_prog_size); util_assert(sizeof(buf) % min_prog_size == 0); util_assert(sector_size >= min_prog_size); util_assert(sector_size % min_prog_size == 0); // Setup global variables current_sector_addr = ROUND_DOWN(addr, sector_size); current_sector_size = sector_size; current_write_block_addr = current_sector_addr; current_write_block_size = MIN(sector_size, sizeof(buf)); if(page_erase_enabled) { // Erase the current sector status = intf->erase_sector(current_sector_addr); flash_manager_printf(" intf->erase_sector(addr=0x%x) ret=%i\r\n", current_sector_addr); if (ERROR_SUCCESS != status) { intf->uninit(); return status; } } // Clear out buffer in case block size changed memset(buf, 0xFF, current_write_block_size); flash_manager_printf(" setup_next_sector(addr=0x%x) sect_addr=0x%x, write_addr=0x%x,\r\n", addr, current_sector_addr, current_write_block_addr); flash_manager_printf(" actual_write_size=0x%x, sector_size=0x%x, min_write=0x%x\r\n", current_write_block_size, current_sector_size, min_prog_size); return ERROR_SUCCESS; }
static error_t flush_current_block(uint32_t addr){ // Write out current buffer if there is data in it error_t status = ERROR_SUCCESS; if (!buf_empty) { status = intf->program_page(current_write_block_addr, buf, current_write_block_size); flash_manager_printf(" intf->program_page(addr=0x%x, size=0x%x) ret=%i\r\n", current_write_block_addr, current_write_block_size, status); buf_empty = true; } // Setup for next block memset(buf, 0xFF, current_write_block_size); current_write_block_addr = ROUND_DOWN(addr,current_write_block_size); return status; }
error_t flash_manager_data(uint32_t addr, const uint8_t *data, uint32_t size) { uint32_t size_left; uint32_t copy_size; uint32_t pos; error_t status = ERROR_SUCCESS; flash_manager_printf("flash_manager_data(addr=0x%x size=0x%x)\r\n", addr, size); if (state != STATE_OPEN) { util_assert(0); return ERROR_INTERNAL; } // Enforce that addresses are sequential. Currently flash manager // only supports sequential addresses. In the future flash manager // could be updated to support this. if (addr < last_addr) { util_assert(0); state = STATE_ERROR; return ERROR_INTERNAL; } // Setup the current sector if it is not setup already if (!current_sector_valid) { status = setup_next_sector(addr); if (ERROR_SUCCESS != status) { state = STATE_ERROR; return status; } current_sector_valid = true; } while (true) { // flush if necessary if (addr >= current_write_block_addr + current_write_block_size) { // Write out current buffer status = intf->program_page(current_write_block_addr, buf, current_write_block_size); flash_manager_printf(" intf->program_page(addr=0x%x, size=0x%x) ret=%i\r\n", current_write_block_addr, current_write_block_size, status); if (ERROR_SUCCESS != status) { state = STATE_ERROR; return status; } // Setup for next page memset(buf, 0xFF, current_write_block_size); buf_empty = true; current_write_block_addr += current_write_block_size; } // Check for end if (size <= 0) { break; } // Change sector if necessary if (addr >= current_sector_addr + current_sector_size) { status = setup_next_sector(addr); if (ERROR_SUCCESS != status) { state = STATE_ERROR; return status; } } // write buffer pos = addr - current_write_block_addr; size_left = current_write_block_size - pos; copy_size = MIN(size, size_left); memcpy(buf + pos, data, copy_size); buf_empty = copy_size == 0; // Update variables addr += copy_size; data += copy_size; size -= copy_size; } last_addr = addr; return status; }
error_t flash_manager_data(uint32_t addr, const uint8_t *data, uint32_t size) { uint32_t size_left; uint32_t copy_size; uint32_t pos; error_t status = ERROR_SUCCESS; flash_manager_printf("flash_manager_data(addr=0x%x size=0x%x)\r\n", addr, size); if (state != STATE_OPEN) { util_assert(0); return ERROR_INTERNAL; } // Setup the current sector if it is not setup already if (!current_sector_valid) { status = setup_next_sector(addr); if (ERROR_SUCCESS != status) { state = STATE_ERROR; return status; } current_sector_valid = true; last_addr = addr; } //non-increasing address support if (ROUND_DOWN(addr, current_write_block_size) != ROUND_DOWN(last_addr, current_write_block_size)) { status = flush_current_block(addr); if (ERROR_SUCCESS != status) { state = STATE_ERROR; return status; } } if (ROUND_DOWN(addr, current_sector_size) != ROUND_DOWN(last_addr, current_sector_size)) { status = setup_next_sector(addr); if (ERROR_SUCCESS != status) { state = STATE_ERROR; return status; } } while (true) { // flush if necessary if (addr >= current_write_block_addr + current_write_block_size) { status = flush_current_block(addr); if (ERROR_SUCCESS != status) { state = STATE_ERROR; return status; } } // Check for end if (size <= 0) { break; } // Change sector if necessary if (addr >= current_sector_addr + current_sector_size) { status = setup_next_sector(addr); if (ERROR_SUCCESS != status) { state = STATE_ERROR; return status; } // check is at sector boundary if (addr != current_write_block_addr) { current_write_block_addr = addr & 0xFFFFFFF0; // 16 bytes alignment } } // write buffer pos = addr - current_write_block_addr; size_left = current_write_block_size - pos; copy_size = MIN(size, size_left); memcpy(buf + pos, data, copy_size); buf_empty = copy_size == 0; // Update variables addr += copy_size; data += copy_size; size -= copy_size; } last_addr = addr; return status; }