From 5dfdf2bc05786c3b07cdd35f3d901741003bd702 Mon Sep 17 00:00:00 2001 From: Swift Tian Date: Fri, 26 Apr 2024 16:05:21 +0800 Subject: [PATCH] samples: mspi: Add a mspi flash sample The sample code is copied from spi_flash. To flash API, there is no difference if the bus is spi or mspi. Signed-off-by: Swift Tian --- .../drivers/mspi/mspi_flash/CMakeLists.txt | 13 ++ samples/drivers/mspi/mspi_flash/README.rst | 44 ++++ .../mspi/mspi_flash/boards/apollo3p_evb.conf | 9 + .../mspi_flash/boards/apollo3p_evb.overlay | 171 ++++++++++++++++ samples/drivers/mspi/mspi_flash/prj.conf | 3 + samples/drivers/mspi/mspi_flash/sample.yaml | 20 ++ samples/drivers/mspi/mspi_flash/src/main.c | 189 ++++++++++++++++++ 7 files changed, 449 insertions(+) create mode 100644 samples/drivers/mspi/mspi_flash/CMakeLists.txt create mode 100644 samples/drivers/mspi/mspi_flash/README.rst create mode 100644 samples/drivers/mspi/mspi_flash/boards/apollo3p_evb.conf create mode 100644 samples/drivers/mspi/mspi_flash/boards/apollo3p_evb.overlay create mode 100644 samples/drivers/mspi/mspi_flash/prj.conf create mode 100644 samples/drivers/mspi/mspi_flash/sample.yaml create mode 100644 samples/drivers/mspi/mspi_flash/src/main.c diff --git a/samples/drivers/mspi/mspi_flash/CMakeLists.txt b/samples/drivers/mspi/mspi_flash/CMakeLists.txt new file mode 100644 index 00000000000..efeab2b862d --- /dev/null +++ b/samples/drivers/mspi/mspi_flash/CMakeLists.txt @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(mspi_flash) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + +if(CONFIG_FLASH_MSPI_ATXP032) + target_include_directories(app PRIVATE ${ZEPHYR_BASE}/include/zephyr/drivers) + target_include_directories(app PRIVATE ${ZEPHYR_BASE}/drivers/mspi) +endif() diff --git a/samples/drivers/mspi/mspi_flash/README.rst b/samples/drivers/mspi/mspi_flash/README.rst new file mode 100644 index 00000000000..40bc22c6661 --- /dev/null +++ b/samples/drivers/mspi/mspi_flash/README.rst @@ -0,0 +1,44 @@ +.. zephyr:code-sample:: mspi-nor + :name: JEDEC MSPI-NOR flash + :relevant-api: flash_interface + + Use the flash API to interact with a MSPI NOR serial flash memory device. + +Overview +******** + +This sample demonstrates using the :ref:`flash API ` on a MSPI NOR serial flash +memory device. While trivial it is an example of direct access and +allows confirmation that the flash is working and that automatic power +savings is correctly implemented. + +Building and Running +******************** + +The application will build only for a target that has a :ref:`devicetree ` +``flash0`` alias that refers to an entry with the following bindings as a compatible: + +* :dtcompatible:`jedec,spi-nor`, `ambiq,mspi-device` + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/mspi/mspi_flash + :board: apollo3p_evb + :goals: build flash + :compact: + +Sample Output +============= + +.. code-block:: console + + *** Booting Zephyr OS build zephyr-v2.3.0-2142-gca01d2e1d748 *** + + JEDEC MSPI-NOR flash testing + ========================== + + Test 1: Flash erase + Flash erase succeeded! + + Test 2: Flash write + Attempting to write 4 bytes + Data read matches data written. Good! diff --git a/samples/drivers/mspi/mspi_flash/boards/apollo3p_evb.conf b/samples/drivers/mspi/mspi_flash/boards/apollo3p_evb.conf new file mode 100644 index 00000000000..1ebe0a6dbd3 --- /dev/null +++ b/samples/drivers/mspi/mspi_flash/boards/apollo3p_evb.conf @@ -0,0 +1,9 @@ +# Copyright (c) 2024 Ambiq Micro Inc. +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_FLASH_MSPI_ATXP032=y +CONFIG_LOG=y +CONFIG_LOG_DEFAULT_LEVEL=0 +CONFIG_MSPI_INIT_PRIORITY=40 +CONFIG_FLASH_INIT_PRIORITY=50 +CONFIG_PM_DEVICE=y diff --git a/samples/drivers/mspi/mspi_flash/boards/apollo3p_evb.overlay b/samples/drivers/mspi/mspi_flash/boards/apollo3p_evb.overlay new file mode 100644 index 00000000000..678b2dfbd1c --- /dev/null +++ b/samples/drivers/mspi/mspi_flash/boards/apollo3p_evb.overlay @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2024 Ambiq Micro Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + flash0 = &atxp032; + }; +}; + +&gpio64_95 { + status = "okay"; +}; + +&gpio32_63 { + status = "okay"; +}; + +&mspi1 { + + pinctrl-0 = <&mspi1_default>; + pinctrl-1 = <&mspi1_sleep>; + pinctrl-2 = <&mspi1_psram>; + pinctrl-3 = <&mspi1_flash>; + pinctrl-names = "default","sleep","psram","flash"; + status = "okay"; + + ce-gpios = <&gpio64_95 5 GPIO_ACTIVE_LOW>, + <&gpio32_63 18 GPIO_ACTIVE_LOW>; + + cmdq-buffer-location = ".mspi_buff"; + cmdq-buffer-size = <256>; + + aps6404l: aps6404l@0 { + compatible = "mspi-aps6404l"; + size = ; + reg = <0>; + status = "disabled"; + mspi-max-frequency = <48000000>; + mspi-io-mode = "MSPI_IO_MODE_QUAD"; + mspi-data-rate = "MSPI_DATA_RATE_SINGLE"; + mspi-hardware-ce-num = <0>; + read-command = <0xEB>; + write-command = <0x38>; + command-length = "INSTR_1_BYTE"; + address-length = "ADDR_3_BYTE"; + rx-dummy = <6>; + tx-dummy = <0>; + xip-config = <1 0 0 0>; + ce-break-config = <1024 3>; + ambiq,timing-config-mask = <3>; + ambiq,timing-config = <0 6 0 0 0 0 0 0>; + }; + + atxp032: atxp032@1 { + compatible = "mspi-atxp032"; + size = ; + reg = <1>; + status = "okay"; + mspi-max-frequency = <48000000>; + mspi-io-mode = "MSPI_IO_MODE_OCTAL"; + mspi-data-rate = "MSPI_DATA_RATE_SINGLE"; + mspi-hardware-ce-num = <0>; + read-command = <0x0B>; + write-command = <0x02>; + command-length = "INSTR_1_BYTE"; + address-length = "ADDR_4_BYTE"; + rx-dummy = <8>; + tx-dummy = <0>; + xip-config = <1 0 0 0>; + ce-break-config = <0 0>; + ambiq,timing-config-mask = <3>; + ambiq,timing-config = <0 8 0 0 0 0 0 0>; + }; + +}; + +&pinctrl { + + mspi1_sleep: mspi1_sleep{ + + group1 { + pinmux = , + , + , + , + , + , + , + , + , + , + ; + }; + + }; + + mspi1_psram: mspi1_psram{ + + group1 { + pinmux = , + , + , + , + , + , + , + ; + drive-strength = "0.75"; + ambiq,iom-mspi = <0>; + ambiq,iom-num = <1>; + }; + + group2 { + pinmux = ; + drive-strength = "0.75"; + ambiq,iom-mspi = <0>; + ambiq,iom-num = <2>; + }; + + group3 { + pinmux = ; + drive-strength = "1.0"; + ambiq,iom-mspi = <0>; + ambiq,iom-num = <1>; + }; + + group4 { + pinmux = ; + }; + + }; + + mspi1_flash: mspi1_flash{ + + group1 { + pinmux = , + , + , + , + , + , + , + ; + drive-strength = "0.75"; + ambiq,iom-mspi = <0>; + ambiq,iom-num = <1>; + }; + + group2 { + pinmux = ; + drive-strength = "0.75"; + ambiq,iom-mspi = <0>; + ambiq,iom-num = <2>; + }; + + group3 { + pinmux = ; + drive-strength = "1.0"; + ambiq,iom-mspi = <0>; + ambiq,iom-num = <1>; + }; + + group4 { + pinmux = ; + }; + + }; +}; diff --git a/samples/drivers/mspi/mspi_flash/prj.conf b/samples/drivers/mspi/mspi_flash/prj.conf new file mode 100644 index 00000000000..5f4d3a2d80e --- /dev/null +++ b/samples/drivers/mspi/mspi_flash/prj.conf @@ -0,0 +1,3 @@ +CONFIG_STDOUT_CONSOLE=y +CONFIG_FLASH=y +CONFIG_MSPI=y diff --git a/samples/drivers/mspi/mspi_flash/sample.yaml b/samples/drivers/mspi/mspi_flash/sample.yaml new file mode 100644 index 00000000000..33645362297 --- /dev/null +++ b/samples/drivers/mspi/mspi_flash/sample.yaml @@ -0,0 +1,20 @@ +sample: + name: MSPI Flash Sample +tests: + sample.drivers.mspi.flash: + tags: + - mspi + - flash + filter: dt_compat_enabled("jedec,spi-nor") or dt_compat_enabled("mspi-atxp032") + platform_exclude: hifive_unmatched + harness: console + harness_config: + type: multi_line + ordered: true + regex: + - "Test 1: Flash erase" + - "Flash erase succeeded!" + - "Test 2: Flash write" + - "Attempting to write 4 bytes" + - "Data read matches data written. Good!!" + depends_on: mspi diff --git a/samples/drivers/mspi/mspi_flash/src/main.c b/samples/drivers/mspi/mspi_flash/src/main.c new file mode 100644 index 00000000000..27efefa8af2 --- /dev/null +++ b/samples/drivers/mspi/mspi_flash/src/main.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2024 Ambiq Micro Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#define SPI_FLASH_TEST_REGION_OFFSET 0xff000 + +#define SPI_FLASH_SECTOR_SIZE 4096 + +#define SPI_FLASH_MULTI_SECTOR_TEST + +int single_sector_test(const struct device *flash_dev) +{ + const uint8_t expected[] = { 0x55, 0xaa, 0x66, 0x99 }; + const size_t len = sizeof(expected); + uint8_t buf[sizeof(expected)]; + int rc; + + printf("\nPerform test on single sector"); + /* Write protection needs to be disabled before each write or + * erase, since the flash component turns on write protection + * automatically after completion of write and erase + * operations. + */ + printf("\nTest 1: Flash erase\n"); + + /* Full flash erase if SPI_FLASH_TEST_REGION_OFFSET = 0 and + * SPI_FLASH_SECTOR_SIZE = flash size + */ + rc = flash_erase(flash_dev, SPI_FLASH_TEST_REGION_OFFSET, + SPI_FLASH_SECTOR_SIZE); + if (rc != 0) { + printf("Flash erase failed! %d\n", rc); + } else { + printf("Flash erase succeeded!\n"); + } + + printf("\nTest 2: Flash write\n"); + + printf("Attempting to write %zu bytes\n", len); + rc = flash_write(flash_dev, SPI_FLASH_TEST_REGION_OFFSET, expected, len); + if (rc != 0) { + printf("Flash write failed! %d\n", rc); + return 1; + } + + memset(buf, 0, len); + rc = flash_read(flash_dev, SPI_FLASH_TEST_REGION_OFFSET, buf, len); + if (rc != 0) { + printf("Flash read failed! %d\n", rc); + return 1; + } + + if (memcmp(expected, buf, len) == 0) { + printf("Data read matches data written. Good!!\n"); + } else { + const uint8_t *wp = expected; + const uint8_t *rp = buf; + const uint8_t *rpe = rp + len; + + printf("Data read does not match data written!!\n"); + while (rp < rpe) { + printf("%08x wrote %02x read %02x %s\n", + (uint32_t)(SPI_FLASH_TEST_REGION_OFFSET + (rp - buf)), + *wp, *rp, (*rp == *wp) ? "match" : "MISMATCH"); + ++rp; + ++wp; + } + } + return rc; +} + +#if defined SPI_FLASH_MULTI_SECTOR_TEST +int multi_sector_test(const struct device *flash_dev) +{ + const uint8_t expected[] = { 0x55, 0xaa, 0x66, 0x99 }; + const size_t len = sizeof(expected); + uint8_t buf[sizeof(expected)]; + int rc; + + printf("\nPerform test on multiple consequtive sectors"); + + /* Write protection needs to be disabled before each write or + * erase, since the flash component turns on write protection + * automatically after completion of write and erase + * operations. + */ + printf("\nTest 1: Flash erase\n"); + + /* Full flash erase if SPI_FLASH_TEST_REGION_OFFSET = 0 and + * SPI_FLASH_SECTOR_SIZE = flash size + * Erase 2 sectors for check for erase of consequtive sectors + */ + rc = flash_erase(flash_dev, SPI_FLASH_TEST_REGION_OFFSET, SPI_FLASH_SECTOR_SIZE * 2); + if (rc != 0) { + printf("Flash erase failed! %d\n", rc); + } else { + /* Read the content and check for erased */ + memset(buf, 0, len); + size_t offs = SPI_FLASH_TEST_REGION_OFFSET; + + while (offs < SPI_FLASH_TEST_REGION_OFFSET + 2 * SPI_FLASH_SECTOR_SIZE) { + rc = flash_read(flash_dev, offs, buf, len); + if (rc != 0) { + printf("Flash read failed! %d\n", rc); + return 1; + } + if (buf[0] != 0xff) { + printf("Flash erase failed at offset 0x%x got 0x%x\n", + offs, buf[0]); + return 1; + } + offs += SPI_FLASH_SECTOR_SIZE; + } + printf("Flash erase succeeded!\n"); + } + + printf("\nTest 2: Flash write\n"); + + size_t offs = SPI_FLASH_TEST_REGION_OFFSET; + + while (offs < SPI_FLASH_TEST_REGION_OFFSET + 2 * SPI_FLASH_SECTOR_SIZE) { + printf("Attempting to write %zu bytes at offset 0x%x\n", len, offs); + rc = flash_write(flash_dev, offs, expected, len); + if (rc != 0) { + printf("Flash write failed! %d\n", rc); + return 1; + } + + memset(buf, 0, len); + rc = flash_read(flash_dev, offs, buf, len); + if (rc != 0) { + printf("Flash read failed! %d\n", rc); + return 1; + } + + if (memcmp(expected, buf, len) == 0) { + printf("Data read matches data written. Good!!\n"); + } else { + const uint8_t *wp = expected; + const uint8_t *rp = buf; + const uint8_t *rpe = rp + len; + + printf("Data read does not match data written!!\n"); + while (rp < rpe) { + printf("%08x wrote %02x read %02x %s\n", + (uint32_t)(offs + (rp - buf)), + *wp, *rp, (*rp == *wp) ? "match" : "MISMATCH"); + ++rp; + ++wp; + } + } + offs += SPI_FLASH_SECTOR_SIZE; + } + return rc; +} +#endif + +int main(void) +{ + const struct device *flash_dev = DEVICE_DT_GET(DT_ALIAS(flash0)); + + if (!device_is_ready(flash_dev)) { + printk("%s: device not ready.\n", flash_dev->name); + return 1; + } + + printf("\n%s SPI flash testing\n", flash_dev->name); + printf("==========================\n"); + + if (single_sector_test(flash_dev)) { + return 1; + } +#if defined SPI_FLASH_MULTI_SECTOR_TEST + if (multi_sector_test(flash_dev)) { + return 1; + } +#endif + printf("==========================\n"); + return 0; +}