samples: drivers: Add sample that shows how to use AT45 flash driver
This commit adds a sample application intended to present capabilities
of the flash driver for AT45 family chips and to serve as a reference
of how to make use of that driver.
Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
diff --git a/samples/drivers/spi_flash_at45/CMakeLists.txt b/samples/drivers/spi_flash_at45/CMakeLists.txt
new file mode 100644
index 0000000..cf8b075
--- /dev/null
+++ b/samples/drivers/spi_flash_at45/CMakeLists.txt
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: Apache-2.0
+
+cmake_minimum_required(VERSION 3.13.1)
+include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
+project(spi_flash_at45)
+
+FILE(GLOB app_sources src/*.c)
+target_sources(app PRIVATE ${app_sources})
diff --git a/samples/drivers/spi_flash_at45/README.rst b/samples/drivers/spi_flash_at45/README.rst
new file mode 100644
index 0000000..0222df7
--- /dev/null
+++ b/samples/drivers/spi_flash_at45/README.rst
@@ -0,0 +1,66 @@
+.. _spi_flash_at45_sample:
+
+AT45 DataFlash driver sample
+#############################
+
+Overview
+********
+
+This sample shows how to use the AT45 family DataFlash driver and how to
+specify devicetree nodes that enable flash chip instances to be handled
+by the driver (an overlay with two sample nodes is provided).
+The sample writes a defined test region in the flash memory with bytes of
+increasing (and overflowing at the 8-bit range) values and then reads it back
+to check if the write was successful. The starting value is also increased
+in consecutive runs of the sample. Finally, the flash chip is put into low
+power state.
+In the default configuration, the AT45 flash driver is configured to use
+the Read-Modify-Write functionality of DataFlash chips and does not perform
+page erasing, as it is not needed in this case. This can be modified by
+simply disabling the SPI_FLASH_AT45_USE_READ_MODIFY_WRITE option.
+
+Requirements
+************
+
+This sample has been tested on the NordicSemiconductor nRF9160 DK
+(nrf9160_pca10090) board with the AT45DB321E chip connected.
+It can be easily adjusted to be usable on other boards and with other
+AT45 family chips by just providing a corresponding overlay file.
+
+Building and Running
+********************
+
+The code can be found in :zephyr_file:`samples/drivers/spi_flash_at45`.
+
+To build and flash the application:
+
+.. zephyr-app-commands::
+ :zephyr-app: samples/drivers/spi_flash_at45
+ :board: nrf9160_pca10090
+ :goals: build flash
+ :compact:
+
+Sample Output
+=============
+
+.. code-block:: console
+
+ DataFlash sample app on nrf9160_pca10090
+ Using DATAFLASH_1, chip size: 4194304 bytes (page: 512)
+ Reading the first byte of the test region ... OK
+ Preparing test content starting with 0x01.
+ Writing the first half of the test region... OK
+ Writing the second half of the test region... OK
+ Reading the whole test region... OK
+ Checking the read content... OK
+ Putting the flash device into low power state... OK
+
+The sample is supplied with the overlay file that specifies two instances
+of AT45 family chips but only the one labeled "DATAFLASH_1" is required
+for the sample to work. If the other chip is not connected, the following
+log message appears, but apart from that the behavior of the sample stays
+unaffected.
+
+.. code-block:: console
+
+ [00:00:00.000,000] <err> spi_flash_at45: Wrong JEDEC ID: ff ff ff, expected: 1f 24 00
diff --git a/samples/drivers/spi_flash_at45/nrf9160dk_nrf9160.overlay b/samples/drivers/spi_flash_at45/nrf9160dk_nrf9160.overlay
new file mode 100644
index 0000000..ddaa1a2
--- /dev/null
+++ b/samples/drivers/spi_flash_at45/nrf9160dk_nrf9160.overlay
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+&spi3 {
+ sck-pin = <11>;
+ mosi-pin = <12>;
+ miso-pin = <13>;
+ cs-gpios = <&gpio0 20 0>, <&gpio0 10 0>;
+
+ at45db041e@0 {
+ compatible = "atmel,at45";
+ reg = <0>;
+ spi-max-frequency = <15000000>;
+ label = "DATAFLASH_0";
+ jedec-id = [1f 24 00];
+ size = <4194304>;
+ block-size = <2048>;
+ page-size = <256>;
+ enter-dpd-delay = <2000>;
+ exit-dpd-delay = <35000>;
+ };
+
+ at45db321e@1 {
+ compatible = "atmel,at45";
+ reg = <1>;
+ spi-max-frequency = <15000000>;
+ label = "DATAFLASH_1";
+ jedec-id = [1f 27 01];
+ size = <33554432>;
+ block-size = <4096>;
+ page-size = <512>;
+ use-udpd;
+ enter-dpd-delay = <1000>;
+ exit-dpd-delay = <180000>;
+ };
+};
+
+&uart0 {
+ /*
+ * Only TX is used in this sample, so delete the RX pin assignment
+ * to prevent UART receiver from being enabled and consuming power.
+ */
+ /delete-property/ rx-pin;
+};
diff --git a/samples/drivers/spi_flash_at45/prj.conf b/samples/drivers/spi_flash_at45/prj.conf
new file mode 100644
index 0000000..37cf63a
--- /dev/null
+++ b/samples/drivers/spi_flash_at45/prj.conf
@@ -0,0 +1,10 @@
+CONFIG_STDOUT_CONSOLE=y
+CONFIG_LOG=y
+CONFIG_LOG_IMMEDIATE=y
+
+CONFIG_SPI=y
+CONFIG_FLASH=y
+CONFIG_FLASH_PAGE_LAYOUT=y
+CONFIG_SPI_FLASH_AT45=y
+
+CONFIG_DEVICE_POWER_MANAGEMENT=y
diff --git a/samples/drivers/spi_flash_at45/sample.yaml b/samples/drivers/spi_flash_at45/sample.yaml
new file mode 100644
index 0000000..02a9962
--- /dev/null
+++ b/samples/drivers/spi_flash_at45/sample.yaml
@@ -0,0 +1,6 @@
+sample:
+ name: SPI Flash AT45 Sample
+tests:
+ sample.drivers.spi.flash.at45:
+ tags: spi flash
+ platform_whitelist: nrf9160_pca10090
diff --git a/samples/drivers/spi_flash_at45/src/main.c b/samples/drivers/spi_flash_at45/src/main.c
new file mode 100644
index 0000000..351026b
--- /dev/null
+++ b/samples/drivers/spi_flash_at45/src/main.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2020 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr.h>
+#include <drivers/flash.h>
+#include <logging/log_ctrl.h>
+
+#define FLASH_DEVICE "DATAFLASH_1"
+
+/* Set to 1 to test the chip erase functionality. Please be aware that this
+ * operation takes quite a while (it depends on the chip size, but can easily
+ * take tens of seconds).
+ * Note - erasing of the test region or whole chip is performed only when
+ * CONFIG_SPI_FLASH_AT45_USE_READ_MODIFY_WRITE is not enabled.
+ */
+#define ERASE_WHOLE_CHIP 0
+
+#define TEST_REGION_OFFSET 0xFE00
+#define TEST_REGION_SIZE 0x400
+
+static u8_t write_buf[TEST_REGION_SIZE];
+static u8_t read_buf[TEST_REGION_SIZE];
+
+void main(void)
+{
+ printk("DataFlash sample on %s\n", CONFIG_BOARD);
+
+ struct device *flash_dev;
+ int i;
+ int err;
+ u8_t data;
+ struct flash_pages_info pages_info;
+ size_t page_count, chip_size;
+
+ flash_dev = device_get_binding(FLASH_DEVICE);
+ if (!flash_dev) {
+ printk("Device %s not found!\n", FLASH_DEVICE);
+ return;
+ }
+
+ page_count = flash_get_page_count(flash_dev);
+ (void)flash_get_page_info_by_idx(flash_dev, 0, &pages_info);
+ chip_size = page_count * pages_info.size;
+ printk("Using %s, chip size: %u bytes (page: %u)\n",
+ FLASH_DEVICE, chip_size, pages_info.size);
+
+ printk("Reading the first byte of the test region ... ");
+ err = flash_read(flash_dev, TEST_REGION_OFFSET, &data, 1);
+ if (err != 0) {
+ printk("FAILED\n");
+ return;
+ }
+
+ printk("OK\n");
+
+ ++data;
+ printk("Preparing test content starting with 0x%02X.\n", data);
+ for (i = 0; i < TEST_REGION_SIZE; ++i) {
+ write_buf[i] = (u8_t)(data + i);
+ }
+
+#if !IS_ENABLED(CONFIG_SPI_FLASH_AT45_USE_READ_MODIFY_WRITE)
+ if (ERASE_WHOLE_CHIP) {
+ printk("Erasing the whole chip... ");
+ err = flash_erase(flash_dev, 0, chip_size);
+ } else {
+ printk("Erasing the test region... ");
+ err = flash_erase(flash_dev,
+ TEST_REGION_OFFSET, TEST_REGION_SIZE);
+ }
+
+ if (err != 0) {
+ printk("FAILED\n");
+ return;
+ }
+
+ printk("OK\n");
+
+ printk("Checking if the test region is erased... ");
+ err = flash_read(flash_dev, TEST_REGION_OFFSET,
+ read_buf, TEST_REGION_SIZE);
+ if (err != 0) {
+ printk("FAILED\n");
+ return;
+ }
+
+ for (i = 0; i < TEST_REGION_SIZE; ++i) {
+ if (read_buf[i] != 0xFF) {
+ printk("\nERROR at read_buf[%d]: "
+ "expected 0x%02X, got 0x%02X\n",
+ i, 0xFF, read_buf[i]);
+ return;
+ }
+ }
+
+ printk("OK\n");
+#endif /* !IS_ENABLED(CONFIG_SPI_FLASH_AT45_USE_READ_MODIFY_WRITE) */
+
+ printk("Writing the first half of the test region... ");
+ err = flash_write(flash_dev, TEST_REGION_OFFSET,
+ write_buf, TEST_REGION_SIZE/2);
+ if (err != 0) {
+ printk("FAILED\n");
+ return;
+ }
+
+ printk("OK\n");
+
+ printk("Writing the second half of the test region... ");
+ err = flash_write(flash_dev, TEST_REGION_OFFSET + TEST_REGION_SIZE/2,
+ &write_buf[TEST_REGION_SIZE/2], TEST_REGION_SIZE/2);
+ if (err != 0) {
+ printk("FAILED\n");
+ return;
+ }
+
+ printk("OK\n");
+
+ printk("Reading the whole test region... ");
+ err = flash_read(flash_dev, TEST_REGION_OFFSET,
+ read_buf, TEST_REGION_SIZE);
+ if (err != 0) {
+ printk("FAILED\n");
+ return;
+ }
+
+ printk("OK\n");
+
+ printk("Checking the read content... ");
+ for (i = 0; i < TEST_REGION_SIZE; ++i) {
+ if (read_buf[i] != write_buf[i]) {
+ printk("\nERROR at read_buf[%d]: "
+ "expected 0x%02X, got 0x%02X\n",
+ i, write_buf[i], read_buf[i]);
+ return;
+ }
+ }
+
+ printk("OK\n");
+
+#if IS_ENABLED(CONFIG_DEVICE_POWER_MANAGEMENT)
+ printk("Putting the flash device into low power state... ");
+ err = device_set_power_state(flash_dev, DEVICE_PM_LOW_POWER_STATE,
+ NULL, NULL);
+ if (err != 0) {
+ printk("FAILED\n");
+ return;
+ }
+
+ printk("OK\n");
+#endif
+
+ k_sleep(K_FOREVER);
+}