diff --git a/samples/drivers/jesd216/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay b/samples/drivers/jesd216/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay new file mode 100644 index 00000000000..4a23938aca1 --- /dev/null +++ b/samples/drivers/jesd216/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&mx25r64 { + status = "okay"; +}; + +/ { + aliases { + jedec-spi-nor = &mx25r64; + }; +}; diff --git a/samples/drivers/spi_flash/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay b/samples/drivers/spi_flash/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay new file mode 100644 index 00000000000..4a23938aca1 --- /dev/null +++ b/samples/drivers/spi_flash/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&mx25r64 { + status = "okay"; +}; + +/ { + aliases { + jedec-spi-nor = &mx25r64; + }; +}; diff --git a/tests/drivers/flash/common/boards/nrf54h20dk_nrf54h20_spi_nor.conf b/tests/drivers/flash/common/boards/nrf54h20dk_nrf54h20_spi_nor.conf new file mode 100644 index 00000000000..cd95775382d --- /dev/null +++ b/tests/drivers/flash/common/boards/nrf54h20dk_nrf54h20_spi_nor.conf @@ -0,0 +1,5 @@ +# Minimal configuration for testing flash driver +# on nrf52840dk/nrf52840 board + +CONFIG_SPI=y +CONFIG_SPI_NOR=y diff --git a/tests/drivers/flash/common/src/main.c b/tests/drivers/flash/common/src/main.c index d3c5d8b7cf6..c9b1d01a907 100644 --- a/tests/drivers/flash/common/src/main.c +++ b/tests/drivers/flash/common/src/main.c @@ -11,26 +11,26 @@ #include #if defined(CONFIG_NORDIC_QSPI_NOR) -#define TEST_AREA_DEV_NODE DT_INST(0, nordic_qspi_nor) +#define TEST_AREA_DEV_NODE DT_INST(0, nordic_qspi_nor) #elif defined(CONFIG_SPI_NOR) -#define TEST_AREA_DEV_NODE DT_INST(0, jedec_spi_nor) +#define TEST_AREA_DEV_NODE DT_INST(0, jedec_spi_nor) #else -#define TEST_AREA storage_partition +#define TEST_AREA storage_partition #endif /* TEST_AREA is only defined for configurations that realy on * fixed-partition nodes. */ #ifdef TEST_AREA -#define TEST_AREA_OFFSET FIXED_PARTITION_OFFSET(TEST_AREA) -#define TEST_AREA_SIZE FIXED_PARTITION_SIZE(TEST_AREA) -#define TEST_AREA_MAX (TEST_AREA_OFFSET + TEST_AREA_SIZE) -#define TEST_AREA_DEVICE FIXED_PARTITION_DEVICE(TEST_AREA) +#define TEST_AREA_OFFSET FIXED_PARTITION_OFFSET(TEST_AREA) +#define TEST_AREA_SIZE FIXED_PARTITION_SIZE(TEST_AREA) +#define TEST_AREA_MAX (TEST_AREA_OFFSET + TEST_AREA_SIZE) +#define TEST_AREA_DEVICE FIXED_PARTITION_DEVICE(TEST_AREA) #elif defined(TEST_AREA_DEV_NODE) -#define TEST_AREA_DEVICE DEVICE_DT_GET(TEST_AREA_DEV_NODE) -#define TEST_AREA_OFFSET 0xff000 +#define TEST_AREA_DEVICE DEVICE_DT_GET(TEST_AREA_DEV_NODE) +#define TEST_AREA_OFFSET 0xff000 #if DT_NODE_HAS_PROP(TEST_AREA_DEV_NODE, size_in_bytes) #define TEST_AREA_MAX DT_PROP(TEST_AREA_DEV_NODE, size_in_bytes) @@ -42,7 +42,11 @@ #error "Unsupported configuraiton" #endif -#define EXPECTED_SIZE 512 +#define EXPECTED_SIZE 512 + +#if !defined(CONFIG_FLASH_HAS_EXPLICIT_ERASE) && !defined(CONFIG_FLASH_HAS_NO_EXPLICIT_ERASE) +#error There is no flash device enabled or it is missing Kconfig options +#endif static const struct device *const flash_dev = TEST_AREA_DEVICE; static struct flash_pages_info page_info; @@ -57,18 +61,35 @@ static void *flash_driver_setup(void) TC_PRINT("Test will run on device %s\n", flash_dev->name); zassert_true(device_is_ready(flash_dev)); - flash_params = flash_get_parameters(flash_dev); - erase_value = flash_params->erase_value; + /* Check for erase is only needed when there is mix of devices */ + if (IS_ENABLED(CONFIG_FLASH_HAS_EXPLICIT_ERASE)) { + const struct flash_parameters *fparams = flash_get_parameters(flash_dev); + + rc = flash_get_page_count(flash_dev); + zassert_not_equal(rc, 0); + TC_PRINT("Memory pages count: %d\n", rc); + + erase_value = fparams->erase_value; + ebw_required = flash_params_get_erase_cap(fparams) & FLASH_ERASE_C_EXPLICIT; + /* For tests purposes use page (in nrf_qspi_nor page = 64 kB) */ + flash_get_page_info_by_offs(flash_dev, TEST_AREA_OFFSET, &page_info); + + rc = flash_get_page_info_by_idx(flash_dev, page_info.index, &page_info); + zassert_ok(rc); + TC_PRINT("Test page info: (start_offset %ld, size %d, index %d)\n", + page_info.start_offset, (uint8_t)page_info.size, page_info.index); - /* For tests purposes use page (in nrf_qspi_nor page = 64 kB) */ - flash_get_page_info_by_offs(flash_dev, TEST_AREA_OFFSET, - &page_info); + } else { + TC_PRINT("No devices with erase requirement present\n"); + erase_value = 0x55; + page_info.start_offset = TEST_AREA_OFFSET; + page_info.size = TEST_AREA_MAX - TEST_AREA_OFFSET; + } /* Check if test region is not empty */ uint8_t buf[EXPECTED_SIZE]; - rc = flash_read(flash_dev, TEST_AREA_OFFSET, - buf, EXPECTED_SIZE); + rc = flash_read(flash_dev, TEST_AREA_OFFSET, buf, EXPECTED_SIZE); zassert_equal(rc, 0, "Cannot read flash"); /* Fill test buffer with random data */ @@ -94,12 +115,11 @@ static void *flash_driver_setup(void) } } - if (!is_buf_clear) { - /* Erase a nb of pages aligned to the EXPECTED_SIZE */ - rc = flash_erase(flash_dev, page_info.start_offset, - (page_info.size * - ((EXPECTED_SIZE + page_info.size - 1) - / page_info.size))); + if (!is_buf_clear) { + /* Erase a nb of pages aligned to the EXPECTED_SIZE */ + rc = flash_erase(flash_dev, page_info.start_offset, + (page_info.size * + ((EXPECTED_SIZE + page_info.size - 1) / page_info.size))); zassert_equal(rc, 0, "Flash memory not properly erased"); } @@ -112,10 +132,21 @@ ZTEST(flash_driver, test_read_unaligned_address) int rc; uint8_t buf[EXPECTED_SIZE]; const uint8_t canary = erase_value; + uint32_t start; + + if (IS_ENABLED(CONFIG_FLASH_HAS_EXPLICIT_ERASE) && ebw_required) { + start = page_info.start_offset; + /* Erase a nb of pages aligned to the EXPECTED_SIZE */ + rc = flash_erase( + flash_dev, page_info.start_offset, + (page_info.size * ((EXPECTED_SIZE + page_info.size - 1) / page_info.size))); + + zassert_equal(rc, 0, "Flash memory not properly erased"); + } else { + start = TEST_AREA_OFFSET; + } - rc = flash_write(flash_dev, - page_info.start_offset, - expected, EXPECTED_SIZE); + rc = flash_write(flash_dev, start, expected, EXPECTED_SIZE); zassert_equal(rc, 0, "Cannot write to flash"); /* read buffer length*/ @@ -128,25 +159,209 @@ ZTEST(flash_driver, test_read_unaligned_address) buf[buf_o - 1] = canary; buf[buf_o + len] = canary; memset(buf + buf_o, 0, len); - rc = flash_read(flash_dev, - page_info.start_offset + ad_o, - buf + buf_o, len); + rc = flash_read(flash_dev, start + ad_o, buf + buf_o, len); zassert_equal(rc, 0, "Cannot read flash"); - zassert_equal(memcmp(buf + buf_o, - expected + ad_o, - len), - 0, "Flash read failed at len=%d, " - "ad_o=%d, buf_o=%d", len, ad_o, buf_o); + zassert_equal(memcmp(buf + buf_o, expected + ad_o, len), 0, + "Flash read failed at len=%d, " + "ad_o=%d, buf_o=%d", + len, ad_o, buf_o); /* check buffer guards */ zassert_equal(buf[buf_o - 1], canary, - "Buffer underflow at len=%d, " - "ad_o=%d, buf_o=%d", len, ad_o, buf_o); + "Buffer underflow at len=%d, " + "ad_o=%d, buf_o=%d", + len, ad_o, buf_o); zassert_equal(buf[buf_o + len], canary, - "Buffer overflow at len=%d, " - "ad_o=%d, buf_o=%d", len, ad_o, buf_o); + "Buffer overflow at len=%d, " + "ad_o=%d, buf_o=%d", + len, ad_o, buf_o); } } } } +ZTEST(flash_driver, test_flash_fill) +{ + uint8_t buf[EXPECTED_SIZE]; + int rc; + off_t i; + + if (IS_ENABLED(CONFIG_FLASH_HAS_EXPLICIT_ERASE) && ebw_required) { + /* Erase a nb of pages aligned to the EXPECTED_SIZE */ + rc = flash_erase( + flash_dev, page_info.start_offset, + (page_info.size * ((EXPECTED_SIZE + page_info.size - 1) / page_info.size))); + + zassert_equal(rc, 0, "Flash memory not properly erased"); + } else { + rc = flash_fill( + flash_dev, 0x55, page_info.start_offset, + (page_info.size * ((EXPECTED_SIZE + page_info.size - 1) / page_info.size))); + zassert_equal(rc, 0, "Leveling memory with fill failed\n"); + } + + /* Fill the device with 0xaa */ + rc = flash_fill(flash_dev, 0xaa, page_info.start_offset, + (page_info.size * ((EXPECTED_SIZE + page_info.size - 1) / page_info.size))); + zassert_equal(rc, 0, "Fill failed\n"); + + rc = flash_read(flash_dev, TEST_AREA_OFFSET, buf, EXPECTED_SIZE); + zassert_equal(rc, 0, "Cannot read flash"); + + for (i = 0; i < EXPECTED_SIZE; i++) { + if (buf[i] != 0xaa) { + break; + } + } + zassert_equal(i, EXPECTED_SIZE, "Expected device to be filled wth 0xaa"); +} + +ZTEST(flash_driver, test_flash_flatten) +{ + uint8_t buf[EXPECTED_SIZE]; + int rc; + off_t i; + + rc = flash_flatten( + flash_dev, page_info.start_offset, + (page_info.size * ((EXPECTED_SIZE + page_info.size - 1) / page_info.size))); + + zassert_equal(rc, 0, "Flash not leveled not properly erased"); + + /* Fill the device with 0xaa */ + rc = flash_fill(flash_dev, 0xaa, page_info.start_offset, + (page_info.size * ((EXPECTED_SIZE + page_info.size - 1) / page_info.size))); + zassert_equal(rc, 0, "Fill failed\n"); + + rc = flash_read(flash_dev, TEST_AREA_OFFSET, buf, EXPECTED_SIZE); + zassert_equal(rc, 0, "Cannot read flash"); + + for (i = 0; i < EXPECTED_SIZE; i++) { + if (buf[i] != 0xaa) { + break; + } + } + zassert_equal(i, EXPECTED_SIZE, "Expected device to be filled wth 0xaa"); +} + +ZTEST(flash_driver, test_flash_erase) +{ + int rc; + uint8_t read_buf[EXPECTED_SIZE]; + bool comparison_result; + const struct flash_parameters *fparams = flash_get_parameters(flash_dev); + + erase_value = fparams->erase_value; + + /* Write test data */ + rc = flash_write(flash_dev, page_info.start_offset, expected, EXPECTED_SIZE); + zassert_equal(rc, 0, "Cannot write to flash"); + + /* Confirm write operation */ + rc = flash_read(flash_dev, page_info.start_offset, read_buf, EXPECTED_SIZE); + zassert_equal(rc, 0, "Cannot read flash"); + + comparison_result = true; + for (int i = 0; i < EXPECTED_SIZE; i++) { + if (read_buf[i] != expected[i]) { + comparison_result = false; + TC_PRINT("i=%d:\tread_buf[i]=%d\texpected[i]=%d\n", i, read_buf[i], + expected[i]); + } + } + zassert_true(comparison_result, "Write operation failed"); + /* Cross check - confirm that expected data is pseudo-random */ + zassert_not_equal(read_buf[0], expected[1], "These values shall be different"); + + /* Erase a nb of pages aligned to the EXPECTED_SIZE */ + rc = flash_erase( + flash_dev, page_info.start_offset, + (page_info.size * ((EXPECTED_SIZE + page_info.size - 1) / page_info.size))); + zassert_equal(rc, 0, "Flash memory not properly erased"); + + /* Confirm erase operation */ + rc = flash_read(flash_dev, page_info.start_offset, read_buf, EXPECTED_SIZE); + zassert_equal(rc, 0, "Cannot read flash"); + + comparison_result = true; + for (int i = 0; i < EXPECTED_SIZE; i++) { + if (read_buf[i] != erase_value) { + comparison_result = false; + TC_PRINT("i=%d:\tread_buf[i]=%d\texpected=%d\n", i, read_buf[i], + erase_value); + } + } + zassert_true(comparison_result, "Write operation failed"); + /* Cross check - confirm that expected data + * doesn't contain erase_value + */ + zassert_not_equal(expected[0], erase_value, "These values shall be different"); +} + +struct test_cb_data_type { + uint32_t page_counter; /* used to count how many pages was iterated */ + uint32_t exit_page; /* terminate iteration when this page is reached */ +}; + +static bool flash_callback(const struct flash_pages_info *info, void *data) +{ + struct test_cb_data_type *cb_data = (struct test_cb_data_type *)data; + + cb_data->page_counter++; + + if (cb_data->page_counter >= cb_data->exit_page) { + return false; + } + + return true; +} + +ZTEST(flash_driver, test_flash_page_layout) +{ + int rc; + struct flash_pages_info page_info_off = {0}; + struct flash_pages_info page_info_idx = {0}; + size_t page_count; + struct test_cb_data_type test_cb_data = {0}; + +#if !defined(CONFIG_FLASH_PAGE_LAYOUT) + ztest_test_skip(); +#endif + + /* Get page info with flash_get_page_info_by_offs() */ + rc = flash_get_page_info_by_offs(flash_dev, TEST_AREA_OFFSET, &page_info_off); + zassert_true(rc == 0, "flash_get_page_info_by_offs returned %d", rc); + TC_PRINT("start_offset=0x%lx\tsize=%d\tindex=%d\n", page_info_off.start_offset, + (int)page_info_off.size, page_info_off.index); + zassert_true(page_info_off.start_offset >= 0, "start_offset is %d", rc); + zassert_true(page_info_off.size > 0, "size is %d", rc); + zassert_true(page_info_off.index >= 0, "index is %d", rc); + + /* Get info for the same page with flash_get_page_info_by_idx() */ + rc = flash_get_page_info_by_idx(flash_dev, page_info_off.index, &page_info_idx); + zassert_true(rc == 0, "flash_get_page_info_by_offs returned %d", rc); + zassert_equal(page_info_off.start_offset, page_info_idx.start_offset); + zassert_equal(page_info_off.size, page_info_idx.size); + zassert_equal(page_info_off.index, page_info_idx.index); + + page_count = flash_get_page_count(flash_dev); + TC_PRINT("page_count=%d\n", (int)page_count); + zassert_true(page_count > 0, "flash_get_page_count returned %d", rc); + zassert_true(page_count >= page_info_off.index); + + /* Test that callback is executed for every page */ + test_cb_data.exit_page = page_count + 1; + flash_page_foreach(flash_dev, flash_callback, &test_cb_data); + zassert_true(page_count == test_cb_data.page_counter, + "page_count = %d not equal to pages counted with cb = %d", page_count, + test_cb_data.page_counter); + + /* Test that callback can cancell iteration */ + test_cb_data.page_counter = 0; + test_cb_data.exit_page = page_count >> 1; + flash_page_foreach(flash_dev, flash_callback, &test_cb_data); + zassert_true(test_cb_data.exit_page == test_cb_data.page_counter, + "%d pages were iterated while it shall stop on page %d", + test_cb_data.page_counter, test_cb_data.exit_page); +} + ZTEST_SUITE(flash_driver, NULL, flash_driver_setup, NULL, NULL, NULL); diff --git a/tests/drivers/flash/common/testcase.yaml b/tests/drivers/flash/common/testcase.yaml index 5bfd4a20944..4cee4d4bbd3 100644 --- a/tests/drivers/flash/common/testcase.yaml +++ b/tests/drivers/flash/common/testcase.yaml @@ -8,6 +8,11 @@ tests: extra_args: OVERLAY_CONFIG=boards/nrf52840dk_nrf52840_qspi_nor.conf integration_platforms: - nrf52840dk/nrf52840 + drivers.flash.common.nrf_spi_nor: + platform_allow: nrf54h20dk/nrf54h20/cpuapp + extra_args: OVERLAY_CONFIG=boards/nrf54h20dk_nrf54h20_spi_nor.conf + integration_platforms: + - nrf54h20dk/nrf54h20/cpuapp drivers.flash.common.nrf_qspi_nor.size_in_bytes: platform_allow: nrf52840dk/nrf52840 extra_args: