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);
+}