diff --git a/src/qspi_flash.c b/src/qspi_flash.c index bc6095b7..64638904 100644 --- a/src/qspi_flash.c +++ b/src/qspi_flash.c @@ -419,8 +419,8 @@ int spi_flash_write(uint32_t address, const void *data, int len) { int ret = 0; int remaining = len; - uint32_t xferSz, page, pages; - uintptr_t addr; + uint32_t xferSz; + uintptr_t addr = address; uint8_t* ptr = (uint8_t*)data; #ifdef DEBUG_QSPI @@ -437,21 +437,19 @@ int spi_flash_write(uint32_t address, const void *data, int len) return -1; } - /* write by page */ - pages = ((len + (FLASH_PAGE_SIZE-1)) / FLASH_PAGE_SIZE); - for (page = 0; page < pages; page++) { + /* write by page: the device's page program wraps within its own page, so + * each transfer must terminate at the next page boundary */ + while (remaining > 0) { ret = qspi_write_enable(); if (ret != 0) { break; } - xferSz = (uint32_t)remaining; - if (xferSz > FLASH_PAGE_SIZE) { - xferSz = FLASH_PAGE_SIZE; + xferSz = FLASH_PAGE_SIZE - ((uint32_t)addr % FLASH_PAGE_SIZE); + if (xferSz > (uint32_t)remaining) { + xferSz = (uint32_t)remaining; } - addr = address + (page * FLASH_PAGE_SIZE); - /* ------ Write Flash (page at a time) ------ */ ret = qspi_transfer(QSPI_MODE_WRITE, FLASH_WRITE_CMD, addr, QSPI_ADDR_SZ, QSPI_DATA_MODE_SPI, /* Address */ @@ -463,7 +461,7 @@ int spi_flash_write(uint32_t address, const void *data, int len) #ifdef DEBUG_QSPI wolfBoot_printf("QSPI Flash Sector Write: " "Ret %d, Cmd 0x%x, Len %d, %p -> 0x%x\n", - ret, FLASH_WRITE_CMD, xferSz, ptr, address); + ret, FLASH_WRITE_CMD, xferSz, ptr, (uint32_t)addr); #endif if (ret != 0) break; @@ -475,6 +473,7 @@ int spi_flash_write(uint32_t address, const void *data, int len) /* write disable is automatic */ remaining -= (int)xferSz; ptr += xferSz; + addr += xferSz; } return ret; diff --git a/tools/unit-tests/unit-qspi-flash.c b/tools/unit-tests/unit-qspi-flash.c index 344c7272..db6cb828 100644 --- a/tools/unit-tests/unit-qspi-flash.c +++ b/tools/unit-tests/unit-qspi-flash.c @@ -113,6 +113,32 @@ START_TEST(test_qspi_write_splits_last_page_to_remaining_bytes) } END_TEST +START_TEST(test_qspi_write_clips_first_page_at_page_boundary) +{ + uint8_t buf[FLASH_PAGE_SIZE + 32]; + uint32_t off = FLASH_PAGE_SIZE - 16; + int ret; + + memset(buf, 0x5A, sizeof(buf)); + + /* Start 16 bytes before a page boundary: the device's page program wraps + * within its own page, so the first transfer must stop at the boundary. */ + ret = spi_flash_write(0x1000 + off, buf, sizeof(buf)); + + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(program_call_count, 3); + ck_assert_uint_eq(program_addrs[0], 0x1000 + off); + ck_assert_uint_eq(program_sizes[0], 16); + ck_assert_ptr_eq(program_ptrs[0], buf); + ck_assert_uint_eq(program_addrs[1], 0x1000 + FLASH_PAGE_SIZE); + ck_assert_uint_eq(program_sizes[1], FLASH_PAGE_SIZE); + ck_assert_ptr_eq(program_ptrs[1], buf + 16); + ck_assert_uint_eq(program_addrs[2], 0x1000 + (FLASH_PAGE_SIZE * 2)); + ck_assert_uint_eq(program_sizes[2], 16); + ck_assert_ptr_eq(program_ptrs[2], buf + 16 + FLASH_PAGE_SIZE); +} +END_TEST + START_TEST(test_qspi_write_stops_after_midloop_write_enable_failure) { uint8_t buf[FLASH_PAGE_SIZE * 3]; @@ -176,6 +202,7 @@ static Suite *qspi_flash_suite(void) tc = tcase_create("Write"); tcase_add_checked_fixture(tc, setup, NULL); tcase_add_test(tc, test_qspi_write_splits_last_page_to_remaining_bytes); + tcase_add_test(tc, test_qspi_write_clips_first_page_at_page_boundary); tcase_add_test(tc, test_qspi_write_stops_after_midloop_write_enable_failure); tcase_add_test(tc, test_qspi_read_rejects_address_at_device_size); tcase_add_test(tc, test_qspi_read_rejects_transfer_extending_past_device_size);