|  | /* | 
|  | * Copyright (c) 2017-2023 Nordic Semiconductor ASA | 
|  | * Copyright (c) 2016 Linaro Limited | 
|  | * Copyright (c) 2016 Intel Corporation | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <errno.h> | 
|  |  | 
|  | #include <zephyr/kernel.h> | 
|  | #include <zephyr/device.h> | 
|  | #include <zephyr/init.h> | 
|  | #include <soc.h> | 
|  | #include <zephyr/drivers/flash.h> | 
|  | #include <string.h> | 
|  | #include <nrfx_nvmc.h> | 
|  | #include <nrf_erratas.h> | 
|  |  | 
|  | #include "soc_flash_nrf.h" | 
|  |  | 
|  | #define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL | 
|  | #include <zephyr/logging/log.h> | 
|  | LOG_MODULE_REGISTER(flash_nrf); | 
|  |  | 
|  | #if DT_NODE_HAS_STATUS(DT_INST(0, nordic_nrf51_flash_controller), okay) | 
|  | #define DT_DRV_COMPAT nordic_nrf51_flash_controller | 
|  | #elif DT_NODE_HAS_STATUS(DT_INST(0, nordic_nrf52_flash_controller), okay) | 
|  | #define DT_DRV_COMPAT nordic_nrf52_flash_controller | 
|  | #elif DT_NODE_HAS_STATUS(DT_INST(0, nordic_nrf53_flash_controller), okay) | 
|  | #define DT_DRV_COMPAT nordic_nrf53_flash_controller | 
|  | #elif DT_NODE_HAS_STATUS(DT_INST(0, nordic_nrf91_flash_controller), okay) | 
|  | #define DT_DRV_COMPAT nordic_nrf91_flash_controller | 
|  | #else | 
|  | #error No matching compatible for soc_flash_nrf.c | 
|  | #endif | 
|  |  | 
|  | #define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash) | 
|  |  | 
|  | #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE | 
|  | #define FLASH_SLOT_WRITE     7500 | 
|  | #if defined(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE) | 
|  | #define FLASH_SLOT_ERASE (MAX(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE_MS * 1000, \ | 
|  | 7500)) | 
|  | #else | 
|  | #define FLASH_SLOT_ERASE FLASH_PAGE_ERASE_MAX_TIME_US | 
|  | #endif /* CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE */ | 
|  |  | 
|  | static int write_op(void *context); /* instance of flash_op_handler_t */ | 
|  | static int write_synchronously(off_t addr, const void *data, size_t len); | 
|  |  | 
|  | static int erase_op(void *context); /* instance of flash_op_handler_t */ | 
|  | static int erase_synchronously(uint32_t addr, uint32_t size); | 
|  | #endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */ | 
|  |  | 
|  | static const struct flash_parameters flash_nrf_parameters = { | 
|  | #if defined(CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS) | 
|  | .write_block_size = 1, | 
|  | #else | 
|  | .write_block_size = 4, | 
|  | #endif | 
|  | .erase_value = 0xff, | 
|  | }; | 
|  |  | 
|  | #if defined(CONFIG_MULTITHREADING) | 
|  | /* semaphore for locking flash resources (tickers) */ | 
|  | static struct k_sem sem_lock; | 
|  | #define SYNC_INIT() k_sem_init(&sem_lock, 1, 1) | 
|  | #define SYNC_LOCK() k_sem_take(&sem_lock, K_FOREVER) | 
|  | #define SYNC_UNLOCK() k_sem_give(&sem_lock) | 
|  | #else | 
|  | #define SYNC_INIT() | 
|  | #define SYNC_LOCK() | 
|  | #define SYNC_UNLOCK() | 
|  | #endif | 
|  |  | 
|  | #if NRF52_ERRATA_242_PRESENT | 
|  | #include <hal/nrf_power.h> | 
|  | static int suspend_pofwarn(void); | 
|  | static void restore_pofwarn(void); | 
|  |  | 
|  | #define SUSPEND_POFWARN() suspend_pofwarn() | 
|  | #define RESUME_POFWARN()  restore_pofwarn() | 
|  | #else | 
|  | #define SUSPEND_POFWARN() 0 | 
|  | #define RESUME_POFWARN() | 
|  | #endif /* NRF52_ERRATA_242_PRESENT */ | 
|  |  | 
|  | static int write(off_t addr, const void *data, size_t len); | 
|  | static int erase(uint32_t addr, uint32_t size); | 
|  |  | 
|  | static inline bool is_aligned_32(uint32_t data) | 
|  | { | 
|  | return (data & 0x3) ? false : true; | 
|  | } | 
|  |  | 
|  | static inline bool is_within_bounds(off_t addr, size_t len, off_t boundary_start, | 
|  | size_t boundary_size) | 
|  | { | 
|  | return (addr >= boundary_start && | 
|  | (addr < (boundary_start + boundary_size)) && | 
|  | (len <= (boundary_start + boundary_size - addr))); | 
|  | } | 
|  |  | 
|  | static inline bool is_regular_addr_valid(off_t addr, size_t len) | 
|  | { | 
|  | return is_within_bounds(addr, len, 0, nrfx_nvmc_flash_size_get()); | 
|  | } | 
|  |  | 
|  | static inline bool is_uicr_addr_valid(off_t addr, size_t len) | 
|  | { | 
|  | #ifdef CONFIG_SOC_FLASH_NRF_UICR | 
|  | return is_within_bounds(addr, len, (off_t)NRF_UICR, sizeof(*NRF_UICR)); | 
|  | #else | 
|  | return false; | 
|  | #endif /* CONFIG_SOC_FLASH_NRF_UICR */ | 
|  | } | 
|  |  | 
|  | #if CONFIG_SOC_FLASH_NRF_UICR && IS_ENABLED(NRF91_ERRATA_7_ENABLE_WORKAROUND) | 
|  | static inline void nrf91_errata_7_enter(void) | 
|  | { | 
|  | __disable_irq(); | 
|  | } | 
|  |  | 
|  | static inline void nrf91_errata_7_exit(void) | 
|  | { | 
|  | __DSB(); | 
|  | __enable_irq(); | 
|  | } | 
|  |  | 
|  | static void nrf_buffer_read_91_uicr(void *data, off_t addr, size_t len) | 
|  | { | 
|  | nrf91_errata_7_enter(); | 
|  | nrf_nvmc_buffer_read(data, (uint32_t)addr, len); | 
|  | nrf91_errata_7_exit(); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void nvmc_wait_ready(void) | 
|  | { | 
|  | while (!nrfx_nvmc_write_done_check()) { | 
|  | } | 
|  | } | 
|  |  | 
|  | static int flash_nrf_read(const struct device *dev, off_t addr, | 
|  | void *data, size_t len) | 
|  | { | 
|  | const bool within_uicr = is_uicr_addr_valid(addr, len); | 
|  |  | 
|  | if (is_regular_addr_valid(addr, len)) { | 
|  | addr += DT_REG_ADDR(SOC_NV_FLASH_NODE); | 
|  | } else if (!within_uicr) { | 
|  | LOG_ERR("invalid address: 0x%08lx:%zu", | 
|  | (unsigned long)addr, len); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (!len) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #if CONFIG_SOC_FLASH_NRF_UICR && IS_ENABLED(NRF91_ERRATA_7_ENABLE_WORKAROUND) | 
|  | if (within_uicr) { | 
|  | nrf_buffer_read_91_uicr(data, (uint32_t)addr, len); | 
|  | return 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | nrf_nvmc_buffer_read(data, (uint32_t)addr, len); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int flash_nrf_write(const struct device *dev, off_t addr, | 
|  | const void *data, size_t len) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | if (is_regular_addr_valid(addr, len)) { | 
|  | addr += DT_REG_ADDR(SOC_NV_FLASH_NODE); | 
|  | } else if (!is_uicr_addr_valid(addr, len)) { | 
|  | LOG_ERR("invalid address: 0x%08lx:%zu", | 
|  | (unsigned long)addr, len); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | #if !IS_ENABLED(CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS) | 
|  | if (!is_aligned_32(addr) || (len % sizeof(uint32_t))) { | 
|  | LOG_ERR("not word-aligned: 0x%08lx:%zu", | 
|  | (unsigned long)addr, len); | 
|  | return -EINVAL; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (!len) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | SYNC_LOCK(); | 
|  |  | 
|  | #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE | 
|  | if (nrf_flash_sync_is_required()) { | 
|  | ret = write_synchronously(addr, data, len); | 
|  | } else | 
|  | #endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */ | 
|  | { | 
|  | ret = write(addr, data, len); | 
|  | } | 
|  |  | 
|  | SYNC_UNLOCK(); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int flash_nrf_erase(const struct device *dev, off_t addr, size_t size) | 
|  | { | 
|  | uint32_t pg_size = nrfx_nvmc_flash_page_size_get(); | 
|  | uint32_t n_pages = size / pg_size; | 
|  | int ret; | 
|  |  | 
|  | if (is_regular_addr_valid(addr, size)) { | 
|  | /* Erase can only be done per page */ | 
|  | if (((addr % pg_size) != 0) || ((size % pg_size) != 0)) { | 
|  | LOG_ERR("unaligned address: 0x%08lx:%zu", | 
|  | (unsigned long)addr, size); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (!n_pages) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | addr += DT_REG_ADDR(SOC_NV_FLASH_NODE); | 
|  | #ifdef CONFIG_SOC_FLASH_NRF_UICR | 
|  | } else if (addr != (off_t)NRF_UICR || size != sizeof(*NRF_UICR)) { | 
|  | LOG_ERR("invalid address: 0x%08lx:%zu", | 
|  | (unsigned long)addr, size); | 
|  | return -EINVAL; | 
|  | } | 
|  | #else | 
|  | } else { | 
|  | LOG_ERR("invalid address: 0x%08lx:%zu", | 
|  | (unsigned long)addr, size); | 
|  | return -EINVAL; | 
|  | } | 
|  | #endif /* CONFIG_SOC_FLASH_NRF_UICR */ | 
|  |  | 
|  | SYNC_LOCK(); | 
|  |  | 
|  | #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE | 
|  | if (nrf_flash_sync_is_required()) { | 
|  | ret = erase_synchronously(addr, size); | 
|  | } else | 
|  | #endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */ | 
|  | { | 
|  | ret = erase(addr, size); | 
|  | } | 
|  |  | 
|  | SYNC_UNLOCK(); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | #if defined(CONFIG_FLASH_PAGE_LAYOUT) | 
|  | static struct flash_pages_layout dev_layout; | 
|  |  | 
|  | static void flash_nrf_pages_layout(const struct device *dev, | 
|  | const struct flash_pages_layout **layout, | 
|  | size_t *layout_size) | 
|  | { | 
|  | *layout = &dev_layout; | 
|  | *layout_size = 1; | 
|  | } | 
|  | #endif /* CONFIG_FLASH_PAGE_LAYOUT */ | 
|  |  | 
|  | static const struct flash_parameters * | 
|  | flash_nrf_get_parameters(const struct device *dev) | 
|  | { | 
|  | ARG_UNUSED(dev); | 
|  |  | 
|  | return &flash_nrf_parameters; | 
|  | } | 
|  |  | 
|  | static const struct flash_driver_api flash_nrf_api = { | 
|  | .read = flash_nrf_read, | 
|  | .write = flash_nrf_write, | 
|  | .erase = flash_nrf_erase, | 
|  | .get_parameters = flash_nrf_get_parameters, | 
|  | #if defined(CONFIG_FLASH_PAGE_LAYOUT) | 
|  | .page_layout = flash_nrf_pages_layout, | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | static int nrf_flash_init(const struct device *dev) | 
|  | { | 
|  | SYNC_INIT(); | 
|  |  | 
|  | #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE | 
|  | nrf_flash_sync_init(); | 
|  | #endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */ | 
|  |  | 
|  | #if defined(CONFIG_FLASH_PAGE_LAYOUT) | 
|  | dev_layout.pages_count = nrfx_nvmc_flash_page_count_get(); | 
|  | dev_layout.pages_size = nrfx_nvmc_flash_page_size_get(); | 
|  | #endif | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | DEVICE_DT_INST_DEFINE(0, nrf_flash_init, NULL, | 
|  | NULL, NULL, | 
|  | POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, | 
|  | &flash_nrf_api); | 
|  |  | 
|  | #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE | 
|  |  | 
|  | static int erase_synchronously(uint32_t addr, uint32_t size) | 
|  | { | 
|  | struct flash_context context = { | 
|  | .flash_addr = addr, | 
|  | .len = size, | 
|  | .enable_time_limit = 1, /* enable time limit */ | 
|  | #if defined(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE) | 
|  | .flash_addr_next = addr | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | struct flash_op_desc flash_op_desc = { | 
|  | .handler = erase_op, | 
|  | .context = &context | 
|  | }; | 
|  |  | 
|  | nrf_flash_sync_set_context(FLASH_SLOT_ERASE); | 
|  | return nrf_flash_sync_exe(&flash_op_desc); | 
|  | } | 
|  |  | 
|  | static int write_synchronously(off_t addr, const void *data, size_t len) | 
|  | { | 
|  | struct flash_context context = { | 
|  | .data_addr = (uint32_t) data, | 
|  | .flash_addr = addr, | 
|  | .len = len, | 
|  | .enable_time_limit = 1 /* enable time limit */ | 
|  | }; | 
|  |  | 
|  | struct flash_op_desc flash_op_desc = { | 
|  | .handler = write_op, | 
|  | .context = &context | 
|  | }; | 
|  |  | 
|  | nrf_flash_sync_set_context(FLASH_SLOT_WRITE); | 
|  | return nrf_flash_sync_exe(&flash_op_desc); | 
|  | } | 
|  |  | 
|  | #endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */ | 
|  |  | 
|  | static int erase_op(void *context) | 
|  | { | 
|  | uint32_t pg_size = nrfx_nvmc_flash_page_size_get(); | 
|  | struct flash_context *e_ctx = context; | 
|  |  | 
|  | #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE | 
|  | uint32_t i = 0U; | 
|  |  | 
|  | if (e_ctx->enable_time_limit) { | 
|  | nrf_flash_sync_get_timestamp_begin(); | 
|  | } | 
|  | #endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */ | 
|  |  | 
|  | #ifdef CONFIG_SOC_FLASH_NRF_UICR | 
|  | if (e_ctx->flash_addr == (off_t)NRF_UICR) { | 
|  | if (SUSPEND_POFWARN()) { | 
|  | return -ECANCELED; | 
|  | } | 
|  |  | 
|  | (void)nrfx_nvmc_uicr_erase(); | 
|  | RESUME_POFWARN(); | 
|  | return FLASH_OP_DONE; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | do { | 
|  | if (SUSPEND_POFWARN()) { | 
|  | return -ECANCELED; | 
|  | } | 
|  |  | 
|  | #if defined(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE) | 
|  | if (e_ctx->flash_addr == e_ctx->flash_addr_next) { | 
|  | nrfx_nvmc_page_partial_erase_init(e_ctx->flash_addr, | 
|  | CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE_MS); | 
|  | e_ctx->flash_addr_next += pg_size; | 
|  | } | 
|  |  | 
|  | if (nrfx_nvmc_page_partial_erase_continue()) { | 
|  | e_ctx->len -= pg_size; | 
|  | e_ctx->flash_addr += pg_size; | 
|  | } | 
|  | #else | 
|  | (void)nrfx_nvmc_page_erase(e_ctx->flash_addr); | 
|  | e_ctx->len -= pg_size; | 
|  | e_ctx->flash_addr += pg_size; | 
|  | #endif /* CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE */ | 
|  |  | 
|  | RESUME_POFWARN(); | 
|  |  | 
|  | #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE | 
|  | i++; | 
|  |  | 
|  | if (e_ctx->enable_time_limit) { | 
|  | if (nrf_flash_sync_check_time_limit(i)) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | } | 
|  | #endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */ | 
|  |  | 
|  | } while (e_ctx->len > 0); | 
|  |  | 
|  | return (e_ctx->len > 0) ? FLASH_OP_ONGOING : FLASH_OP_DONE; | 
|  | } | 
|  |  | 
|  | static void shift_write_context(uint32_t shift, struct flash_context *w_ctx) | 
|  | { | 
|  | w_ctx->flash_addr += shift; | 
|  | w_ctx->data_addr += shift; | 
|  | w_ctx->len -= shift; | 
|  | } | 
|  |  | 
|  | static int write_op(void *context) | 
|  | { | 
|  | struct flash_context *w_ctx = context; | 
|  |  | 
|  | #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE | 
|  | uint32_t i = 1U; | 
|  |  | 
|  | if (w_ctx->enable_time_limit) { | 
|  | nrf_flash_sync_get_timestamp_begin(); | 
|  | } | 
|  | #endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */ | 
|  | #if defined(CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS) | 
|  | /* If not aligned, write unaligned beginning */ | 
|  | if (!is_aligned_32(w_ctx->flash_addr)) { | 
|  | uint32_t count = sizeof(uint32_t) - (w_ctx->flash_addr & 0x3); | 
|  |  | 
|  | if (count > w_ctx->len) { | 
|  | count = w_ctx->len; | 
|  | } | 
|  |  | 
|  | if (SUSPEND_POFWARN()) { | 
|  | return -ECANCELED; | 
|  | } | 
|  |  | 
|  | nrfx_nvmc_bytes_write(w_ctx->flash_addr, | 
|  | (const void *)w_ctx->data_addr, | 
|  | count); | 
|  |  | 
|  | RESUME_POFWARN(); | 
|  | shift_write_context(count, w_ctx); | 
|  |  | 
|  | #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE | 
|  | if (w_ctx->enable_time_limit) { | 
|  | if (nrf_flash_sync_check_time_limit(1)) { | 
|  | nvmc_wait_ready(); | 
|  | return FLASH_OP_ONGOING; | 
|  | } | 
|  | } | 
|  | #endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */ | 
|  | } | 
|  | #endif /* CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS */ | 
|  | /* Write all the 4-byte aligned data */ | 
|  | while (w_ctx->len >= sizeof(uint32_t)) { | 
|  | if (SUSPEND_POFWARN()) { | 
|  | return -ECANCELED; | 
|  | } | 
|  |  | 
|  | nrfx_nvmc_word_write(w_ctx->flash_addr, | 
|  | UNALIGNED_GET((uint32_t *)w_ctx->data_addr)); | 
|  | RESUME_POFWARN(); | 
|  | shift_write_context(sizeof(uint32_t), w_ctx); | 
|  |  | 
|  | #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE | 
|  | i++; | 
|  |  | 
|  | if (w_ctx->enable_time_limit) { | 
|  | if (nrf_flash_sync_check_time_limit(i)) { | 
|  | nvmc_wait_ready(); | 
|  | return FLASH_OP_ONGOING; | 
|  | } | 
|  | } | 
|  | #endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */ | 
|  | } | 
|  | #if defined(CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS) | 
|  | /* Write remaining unaligned data */ | 
|  | if (w_ctx->len) { | 
|  | if (SUSPEND_POFWARN()) { | 
|  | return -ECANCELED; | 
|  | } | 
|  |  | 
|  | nrfx_nvmc_bytes_write(w_ctx->flash_addr, | 
|  | (const void *)w_ctx->data_addr, | 
|  | w_ctx->len); | 
|  | RESUME_POFWARN(); | 
|  | shift_write_context(w_ctx->len, w_ctx); | 
|  | } | 
|  | #endif /* CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS */ | 
|  | nvmc_wait_ready(); | 
|  |  | 
|  | return FLASH_OP_DONE; | 
|  | } | 
|  |  | 
|  | static int erase(uint32_t addr, uint32_t size) | 
|  | { | 
|  | struct flash_context context = { | 
|  | .flash_addr = addr, | 
|  | .len = size, | 
|  | #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE | 
|  | .enable_time_limit = 0, /* disable time limit */ | 
|  | #endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */ | 
|  | #if defined(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE) | 
|  | .flash_addr_next = addr | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | return	erase_op(&context); | 
|  | } | 
|  |  | 
|  | static int write(off_t addr, const void *data, size_t len) | 
|  | { | 
|  | struct flash_context context = { | 
|  | .data_addr = (uint32_t) data, | 
|  | .flash_addr = addr, | 
|  | .len = len, | 
|  | #ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE | 
|  | .enable_time_limit = 0 /* disable time limit */ | 
|  | #endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */ | 
|  | }; | 
|  |  | 
|  | return write_op(&context); | 
|  | } | 
|  |  | 
|  | #if NRF52_ERRATA_242_PRESENT | 
|  | /* Disable POFWARN by writing POFCON before a write or erase operation. | 
|  | * Do not attempt to write or erase if EVENTS_POFWARN is already asserted. | 
|  | */ | 
|  | static bool pofcon_enabled; | 
|  |  | 
|  | static int suspend_pofwarn(void) | 
|  | { | 
|  | if (!nrf52_errata_242()) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | bool enabled; | 
|  | nrf_power_pof_thr_t pof_thr; | 
|  |  | 
|  | pof_thr = nrf_power_pofcon_get(NRF_POWER, &enabled); | 
|  |  | 
|  | if (enabled) { | 
|  | nrf_power_pofcon_set(NRF_POWER, false, pof_thr); | 
|  |  | 
|  | /* This check need to be reworked once POFWARN event will be | 
|  | * served by zephyr. | 
|  | */ | 
|  | if (nrf_power_event_check(NRF_POWER, NRF_POWER_EVENT_POFWARN)) { | 
|  | nrf_power_pofcon_set(NRF_POWER, true, pof_thr); | 
|  | return -ECANCELED; | 
|  | } | 
|  |  | 
|  | pofcon_enabled = enabled; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void restore_pofwarn(void) | 
|  | { | 
|  | nrf_power_pof_thr_t pof_thr; | 
|  |  | 
|  | if (pofcon_enabled) { | 
|  | pof_thr = nrf_power_pofcon_get(NRF_POWER, NULL); | 
|  |  | 
|  | nrf_power_pofcon_set(NRF_POWER, true, pof_thr); | 
|  | pofcon_enabled = false; | 
|  | } | 
|  | } | 
|  | #endif  /* NRF52_ERRATA_242_PRESENT */ |