zephyr: erase flash pages progressively

This commit adds the option to erase flash pages while receiving
the firmware, opposed to bulk-erasing the whole image area at
the beginning of the DFU process. This is required on some
hardware that has long erase times, to prevent a long wait
and possibly a timeout during DFU.

Signed-off-by: Emanuele Di Santo <emdi@nordicsemi.no>
Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
diff --git a/boot/boot_serial/src/boot_serial.c b/boot/boot_serial/src/boot_serial.c
index 74ac828..b2be12b 100644
--- a/boot/boot_serial/src/boot_serial.c
+++ b/boot/boot_serial/src/boot_serial.c
@@ -212,6 +212,10 @@
     char name_str[8];
     const struct flash_area *fap = NULL;
     int rc;
+#ifdef CONFIG_BOOT_ERASE_PROGRESSIVELY
+    static off_t off_last = -1;
+    struct flash_sector sector;
+#endif
 
     memset(img_data, 0, sizeof(img_data));
 
@@ -323,10 +327,12 @@
         if (data_len > fap->fa_size) {
             goto out_invalid_data;
         }
+#ifndef CONFIG_BOOT_ERASE_PROGRESSIVELY
         rc = flash_area_erase(fap, 0, fap->fa_size);
         if (rc) {
             goto out_invalid_data;
         }
+#endif
         img_size = data_len;
     }
     if (off != curr_off) {
@@ -339,6 +345,25 @@
             img_blen -= rem_bytes;
         }
     }
+
+#ifdef CONFIG_BOOT_ERASE_PROGRESSIVELY
+    rc = flash_area_sector_from_off(curr_off + img_blen, &sector);
+    if (rc) {
+        BOOT_LOG_ERR("Unable to determine flash sector size");
+        goto out;
+    }
+    if (off_last != sector.fs_off) {
+        off_last = sector.fs_off;
+        BOOT_LOG_INF("Moving to sector 0x%x", sector.fs_off);
+        rc = flash_area_erase(fap, sector.fs_off, sector.fs_size);
+        if (rc) {
+            BOOT_LOG_ERR("Error %d while erasing sector", rc);
+            goto out;
+        }
+    }
+#endif
+
+    BOOT_LOG_INF("Writing at 0x%x until 0x%x", curr_off, curr_off + img_blen);
     rc = flash_area_write(fap, curr_off, img_data, img_blen);
     if (rc == 0) {
         curr_off += img_blen;
@@ -346,6 +371,7 @@
     out_invalid_data:
         rc = MGMT_ERR_EINVAL;
     }
+
 out:
     BOOT_LOG_INF("RX: 0x%x", rc);
     cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig
index 96b5268..31c78a1 100644
--- a/boot/zephyr/Kconfig
+++ b/boot/zephyr/Kconfig
@@ -75,6 +75,15 @@
 	  memory usage; larger values allow it to support larger images.
 	  If unsure, leave at the default value.
 
+config BOOT_ERASE_PROGRESSIVELY
+	bool "Erase flash progressively when receiving new firmware"
+	default y if SOC_NRF52840
+	help
+	 If enabled, flash is erased as necessary when receiving new firmware,
+	 instead of erasing the whole image slot at once. This is necessary
+	 on some hardware that has long erase times, to prevent long wait
+	 times at the beginning of the DFU process.
+
 config ZEPHYR_TRY_MASS_ERASE
 	bool "Try to mass erase flash when flashing MCUboot image"
 	default y
diff --git a/boot/zephyr/flash_map_extended.c b/boot/zephyr/flash_map_extended.c
index ee2320c..7133c99 100644
--- a/boot/zephyr/flash_map_extended.c
+++ b/boot/zephyr/flash_map_extended.c
@@ -11,7 +11,6 @@
 #include "target.h"
 
 #include <flash_map_backend/flash_map_backend.h>
-#include <hal/hal_flash.h>
 #include <sysflash/sysflash.h>
 
 #include "bootutil/bootutil_log.h"
@@ -24,6 +23,16 @@
 #define FLASH_DEVICE_ID SOC_FLASH_0_ID
 #define FLASH_DEVICE_BASE CONFIG_FLASH_BASE_ADDRESS
 
+static struct device *flash_dev;
+
+struct device *flash_device_get_binding(char *dev_name)
+{
+    if (!flash_dev) {
+        flash_dev = device_get_binding(dev_name);
+    }
+    return flash_dev;
+}
+
 int flash_device_base(uint8_t fd_id, uintptr_t *ret)
 {
     if (fd_id != FLASH_DEVICE_ID) {
@@ -43,3 +52,19 @@
 {
     return slot + FLASH_AREA_IMAGE_0;
 }
+
+int flash_area_sector_from_off(off_t off, struct flash_sector *sector)
+{
+    int rc;
+    struct flash_pages_info page;
+
+    rc = flash_get_page_info_by_offs(flash_dev, off, &page);
+    if (rc) {
+        return rc;
+    }
+
+    sector->fs_off = page.start_offset;
+    sector->fs_size = page.size;
+
+    return rc;
+}
\ No newline at end of file
diff --git a/boot/zephyr/include/flash_map_backend/flash_map_backend.h b/boot/zephyr/include/flash_map_backend/flash_map_backend.h
index 18009c5..d09aa59 100644
--- a/boot/zephyr/include/flash_map_backend/flash_map_backend.h
+++ b/boot/zephyr/include/flash_map_backend/flash_map_backend.h
@@ -8,7 +8,7 @@
 #ifndef __FLASH_MAP_BACKEND_H__
 #define __FLASH_MAP_BACKEND_H__
 
-#include <flash_map.h> // the zephyr falsh_map
+#include <flash_map.h> // the zephyr flash_map
 
 #ifdef __cplusplus
 extern "C" {
@@ -32,7 +32,13 @@
  * and match the target offset specified in download script.
  */
 #include <inttypes.h>
+#include <sys/types.h>
 
+/* Retrieve the flash device with the given name.
+ *
+ * Returns the flash device on success, or NULL on failure.
+ */
+struct device *flash_device_get_binding(char *dev_name);
 
 /*
  * Retrieve a memory-mapped flash device's base address.
@@ -46,6 +52,12 @@
 
 int flash_area_id_from_image_slot(int slot);
 
+/* Retrieve the flash sector a given offset belongs to.
+ *
+ * Returns 0 on success, or an error code on failure.
+ */
+int flash_area_sector_from_off(off_t off, struct flash_sector *sector);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/boot/zephyr/main.c b/boot/zephyr/main.c
index 5633a14..ca30bb5 100644
--- a/boot/zephyr/main.c
+++ b/boot/zephyr/main.c
@@ -39,8 +39,6 @@
 };
 #endif
 
-struct device *boot_flash_device;
-
 void os_heap_init(void);
 
 #if defined(CONFIG_ARM)
@@ -103,8 +101,7 @@
 
     os_heap_init();
 
-    boot_flash_device = device_get_binding(FLASH_DEV_NAME);
-    if (!boot_flash_device) {
+    if (!flash_device_get_binding(FLASH_DEV_NAME)) {
         BOOT_LOG_ERR("Flash device %s not found", FLASH_DEV_NAME);
         while (1)
             ;