text: /*
   * Copyright (c) 2012 - 2019, Nordic Semiconductor ASA
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   *
   * 1. Redistributions of source code must retain the above copyright notice, this
   *    list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   *
   * 3. Neither the name of the copyright holder nor the names of its
   *    contributors may be used to endorse or promote products derived from this
   *    software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   * POSSIBILITY OF SUCH DAMAGE.
   */
  
  #ifndef NRF_NVMC_H__
  #define NRF_NVMC_H__
  
  #include <nrfx.h>
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  /**
   * @defgroup nrf_nvmc_hal_deprecated NVMC HAL (deprecated)
   * @{
   * @ingroup nrf_nvmc
   * @brief   Hardware access layer (HAL) for managing the Non-Volatile Memory Controller (NVMC) peripheral.
   *
   * This driver allows writing to the non-volatile memory (NVM) regions
   * of the chip. To write to NVM, the controller must be powered
   * on and the relevant page must be erased.
   */
  
  /**
   * @brief Erase a page in flash. This is required before writing to any address in the page.
   *
   * @param address Start address of the page.
   */
  void nrf_nvmc_page_erase(uint32_t address);
  
  /**
   * @brief Write a single byte to flash.
   *
   * The function reads the word containing the byte, and then rewrites the entire word.
   *
   * @param address Address to write to.
   * @param value   Value to write.
   */
  void nrf_nvmc_write_byte(uint32_t address , uint8_t value);
  
  /**
   * @brief Write a 32-bit word to flash.
   *
   * @param address Address to write to.
   * @param value   Value to write.
   */
  void nrf_nvmc_write_word(uint32_t address, uint32_t value);
  
  /**
   * @brief Write consecutive bytes to flash.
   *
   * @param address   Address to write to.
   * @param src       Pointer to data to copy from.
   * @param num_bytes Number of bytes in src to write.
   */
  void nrf_nvmc_write_bytes(uint32_t  address, const uint8_t * src, uint32_t num_bytes);
  
  /**
   * @brief Write consecutive words to flash.
   *
   * @param address   Address to write to.
   * @param src       Pointer to data to copy from.
   * @param num_words Number of words in src to write.
   */
  void nrf_nvmc_write_words(uint32_t address, const uint32_t * src, uint32_t num_words);
  
  /** @} */
  
  /**
   * @defgroup nrf_nvmc_hal NVMC HAL
   * @{
   * @ingroup nrf_nvmc
   * @brief   Hardware access layer (HAL) for managing the Non-Volatile Memory Controller (NVMC) peripheral.
   */
  
  #if defined(NVMC_ICACHECNF_CACHEEN_Msk) || defined(__NRFX_DOXYGEN__)
  /** @brief Symbol indicating whether Instruction Cache (ICache) is present. */
  #define NRF_NVMC_ICACHE_PRESENT
  #endif
  
  #if defined(NVMC_ERASEPAGEPARTIALCFG_DURATION_Msk) || defined(__NRFX_DOXYGEN__)
  /** @brief Symbol indicating whether the option of page partial erase is present. */
  #define NRF_NVMC_PARTIAL_ERASE_PRESENT
  #endif
  
  /** @brief NVMC modes. */
  typedef enum
  {
      NRF_NVMC_MODE_READONLY      = NVMC_CONFIG_WEN_Ren, ///< NVMC in read-only mode.
      NRF_NVMC_MODE_WRITE         = NVMC_CONFIG_WEN_Wen, ///< NVMC in read and write mode.
      NRF_NVMC_MODE_ERASE         = NVMC_CONFIG_WEN_Een, ///< NVMC in read and erase mode.
  #if defined(NVMC_CONFIG_WEN_PEen)
      NRF_NVMC_MODE_PARTIAL_ERASE = NVMC_CONFIG_WEN_PEen ///< NVMC in read and partial erase mode.
  #endif
  } nrf_nvmc_mode_t;
  
  #if defined(NVMC_CONFIGNS_WEN_Msk) || defined(__NRFX_DOXYGEN__)
  /** @brief Non-secure NVMC modes. */
  typedef enum
  {
      NRF_NVMC_NS_MODE_READONLY = NVMC_CONFIGNS_WEN_Ren, ///< Non-secure NVMC in read-only mode.
      NRF_NVMC_NS_MODE_WRITE    = NVMC_CONFIGNS_WEN_Wen, ///< Non-secure NVMC in read and write mode.
      NRF_NVMC_NS_MODE_ERASE    = NVMC_CONFIGNS_WEN_Een, ///< Non-secure NVMC in read and erase mode.
  } nrf_nvmc_ns_mode_t;
  #endif
  
  #if defined(NRF_NVMC_ICACHE_PRESENT)
  /** @brief NVMC ICache configuration. */
  typedef enum
  {
      NRF_NVMC_ICACHE_DISABLE               = NVMC_ICACHECNF_CACHEEN_Disabled, ///< Instruction Cache disabled.
      NRF_NVMC_ICACHE_ENABLE                = NVMC_ICACHECNF_CACHEEN_Enabled,  ///< Instruction Cache enabled.
      NRF_NVMC_ICACHE_ENABLE_WITH_PROFILING = NVMC_ICACHECNF_CACHEEN_Enabled | ///< Instruction Cache with cache profiling enabled.
                                              NVMC_ICACHECNF_CACHEPROFEN_Msk
  } nrf_nvmc_icache_config_t;
  #endif // defined(NRF_NVMC_ICACHE_PRESENT)
  
  /**
   * @brief Function for checking if NVMC is ready to perform write or erase operation.
   *
   * @param[in] p_reg Pointer to the structure of registers of the peripheral.
   *
   * @retval true  NVMC can perform write or erase.
   * @retval false NVMC is busy and cannot perform next operation yet.
   */
  __STATIC_INLINE bool nrf_nvmc_ready_check(NRF_NVMC_Type const * p_reg);
  
  #if defined(NVMC_READYNEXT_READYNEXT_Msk) || defined(__NRFX_DOXYGEN__)
  /**
   * @brief Function for checking if NVMC is ready to accept the next write operation.
   *
   * NVM writing time can be reduced by using this function.
   *
   * @param[in] p_reg Pointer to the structure of registers of the peripheral.
   *
   * @retval true  NVMC can accept the next write. It will be buffered and will be taken
   *               into account as soon as the ongoing write operation is completed.
   * @retval false NVMC is busy and cannot accept the next write yet.
   */
  __STATIC_INLINE bool nrf_nvmc_write_ready_check(NRF_NVMC_Type const * p_reg);
  #endif // defined(NVMC_READYNEXT_READYNEXT_Msk) || defined(__NRFX_DOXYGEN__)
  
  /**
   * @brief Function for setting the NVMC mode.
   *
   * Only activate erase and write modes when they are actively used.
   * If Instruction Cache (ICache) is present, enabling write or erase will
   * invalidate the cache and keep it invalidated.
   *
   * @param[in] p_reg Pointer to the structure of registers of the peripheral.
   * @param[in] mode  Desired operating mode for NVMC.
   */
  __STATIC_INLINE void nrf_nvmc_mode_set(NRF_NVMC_Type * p_reg,
                                         nrf_nvmc_mode_t mode);
  
  #if defined(NVMC_CONFIGNS_WEN_Msk) || defined(__NRFX_DOXYGEN__)
  /**
   * @brief Function for setting the NVMC mode for non-secure Flash page operations.
   *
   * @param[in] p_reg Pointer to the structure of registers of the peripheral.
   * @param[in] mode  Desired operating mode for NVMC.
   */
  __STATIC_INLINE void nrf_nvmc_nonsecure_mode_set(NRF_NVMC_Type *    p_reg,
                                                   nrf_nvmc_ns_mode_t mode);
  #endif
  
  /**
   * @brief Function for starting a single page erase in the Flash memory.
   *
   * The NVMC mode must be correctly configured with @ref nrf_nvmc_mode_set
   * before starting the erase operation.
   *
   * @param[in] p_reg     Pointer to the structure of registers of the peripheral.
   * @param[in] page_addr Address of the first word of the page to erase.
   */
  __STATIC_INLINE void nrf_nvmc_page_erase_start(NRF_NVMC_Type * p_reg,
                                                 uint32_t        page_addr);
  
  #if defined(NVMC_ERASEUICR_ERASEUICR_Msk) || defined(__NRFX_DOXYGEN__)
  /**
   * @brief Function for starting the user information configuration registers (UICR) erase.
   *
   * @param[in] p_reg Pointer to the structure of registers of the peripheral.
   */
  __STATIC_INLINE void nrf_nvmc_uicr_erase_start(NRF_NVMC_Type * p_reg);
  #endif
  
  /**
   * @brief Function for starting the erase of the whole NVM, including UICR.
   *
   * This function purges all user code.
   *
   * @param[in] p_reg Pointer to the structure of registers of the peripheral.
   */
  __STATIC_INLINE void nrf_nvmc_erase_all_start(NRF_NVMC_Type * p_reg);
  
  #if defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
  /**
   * @brief Function for configuring the page partial erase duration in milliseconds.
   *
   * @param[in] p_reg       Pointer to the structure of registers of the peripheral.
   * @param[in] duration_ms Page partial erase duration in milliseconds.
   */
  __STATIC_INLINE void nrf_nvmc_partial_erase_duration_set(NRF_NVMC_Type * p_reg,
                                                           uint32_t        duration_ms);
  
  /**
   * @brief Function for getting the current setting for the page partial erase duration.
   *
   * @param[in] p_reg Pointer to the structure of registers of the peripheral.
   *
   * @retval Interval duration setting in milliseconds.
   */
  __STATIC_INLINE uint32_t nrf_nvmc_partial_erase_duration_get(NRF_NVMC_Type const * p_reg);
  
  /**
   * @brief Function for starting a partial erase operation.
   *
   * It must be called successively until the page erase time is reached.
   *
   * @param[in] p_reg     Pointer to the structure of registers of the peripheral.
   * @param[in] page_addr Address of the first word of the page to erase.
   */
  __STATIC_INLINE void nrf_nvmc_page_partial_erase_start(NRF_NVMC_Type * p_reg,
                                                         uint32_t        page_addr);
  #endif // defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
  
  #if defined(NRF_NVMC_ICACHE_PRESENT)
  /**
   * @brief Function for applying the Instruction Cache (ICache) configuration.
   *
   * Enabling the cache can increase CPU performance and reduce power
   * consumption by reducing the number of wait cycles and the number
   * of flash accesses.
   *
   * @param[in] p_reg  Pointer to the structure of registers of the peripheral.
   * @param[in] config ICache configuration.
   */
  __STATIC_INLINE void nrf_nvmc_icache_config_set(NRF_NVMC_Type *          p_reg,
                                                  nrf_nvmc_icache_config_t config);
  
  /**
   * @brief Function for checking if ICache is enabled.
   *
   * @param[in] p_reg Pointer to the structure of registers of the peripheral.
   *
   * @retval true  ICache enabled.
   * @retval false ICache disabled.
   */
  __STATIC_INLINE bool nrf_nvmc_icache_enable_check(NRF_NVMC_Type const * p_reg);
  
  /**
   * @brief Function for checking if the ICache profiling option is enabled.
   *
   * @param[in] p_reg Pointer to the structure of registers of the peripheral.
   *
   * @retval true  ICache profiling enabled.
   * @retval false ICache profiling disabled.
   */
  __STATIC_INLINE bool nrf_nvmc_icache_profiling_enable_check(NRF_NVMC_Type const * p_reg);
  
  /**
   * @brief Function for getting the number of ICache hits.
   *
   * @param[in] p_reg Pointer to the structure of registers of the peripheral.
   *
   * @retval Number of the ICache hits.
   */
  __STATIC_INLINE uint32_t nrf_nvmc_icache_hit_get(NRF_NVMC_Type const * p_reg);
  
  /**
   * @brief Function for getting the number of ICache misses.
   *
   * @param[in] p_reg Pointer to the structure of registers of the peripheral.
   *
   * @retval Number of the ICache misses.
   */
  __STATIC_INLINE uint32_t nrf_nvmc_icache_miss_get(NRF_NVMC_Type const * p_reg);
  
  /**
   * @brief Function for resetting the ICache hit and miss counters.
   *
   * @param[in] p_reg Pointer to the structure of registers of the peripheral.
   */
   __STATIC_INLINE void nrf_nvmc_icache_hit_miss_reset(NRF_NVMC_Type * p_reg);
  #endif // defined(NRF_NVMC_ICACHE_PRESENT)
  
  #ifndef SUPPRESS_INLINE_IMPLEMENTATION
  
  __STATIC_INLINE bool nrf_nvmc_ready_check(NRF_NVMC_Type const * p_reg)
  {
      return (bool)(p_reg->READY & NVMC_READY_READY_Msk);
  }
  
  #if defined(NVMC_READYNEXT_READYNEXT_Msk)
  __STATIC_INLINE bool nrf_nvmc_write_ready_check(NRF_NVMC_Type const * p_reg)
  {
      return (bool)(p_reg->READYNEXT & NVMC_READYNEXT_READYNEXT_Msk);
  }
  #endif
  
  __STATIC_INLINE void nrf_nvmc_mode_set(NRF_NVMC_Type * p_reg,
                                         nrf_nvmc_mode_t mode)
  {
      p_reg->CONFIG = (uint32_t)mode;
  }
  
  #if defined(NVMC_CONFIGNS_WEN_Msk)
  __STATIC_INLINE void nrf_nvmc_nonsecure_mode_set(NRF_NVMC_Type *    p_reg,
                                                   nrf_nvmc_ns_mode_t mode)
  {
      p_reg->CONFIGNS = (uint32_t)mode;
  }
  #endif
  
  __STATIC_INLINE void nrf_nvmc_page_erase_start(NRF_NVMC_Type * p_reg,
                                                 uint32_t        page_addr)
  {
  #if defined(NRF51)
      /* On nRF51, the code area can be divided into two regions: CR0 and CR1.
       * The length of CR0 is specified in the CLENR0 register of UICR.
       * If CLENR0 contains the 0xFFFFFFFF value,  CR0 is not set.
       * Moreover, the page from CR0 can be written or erased only from code
       * running in CR0.*/
      uint32_t cr0_len = NRF_UICR->CLENR0 == 0xFFFFFFFF ? 0 : NRF_UICR->CLENR0;
      if (page_addr < cr0_len)
      {
          p_reg->ERASEPCR0 = page_addr;
      }
      else
      {
          p_reg->ERASEPCR1 = page_addr;
      }
  #elif defined(NRF52_SERIES)
      p_reg->ERASEPAGE = page_addr;
  #elif defined(NRF9160_XXAA)
      *(volatile uint32_t *)page_addr = 0xFFFFFFFF;
      (void)p_reg;
  #else
      #error "Unknown device."
  #endif
  }
  
  #if defined(NVMC_ERASEUICR_ERASEUICR_Msk)
  __STATIC_INLINE void nrf_nvmc_uicr_erase_start(NRF_NVMC_Type * p_reg)
  {
      p_reg->ERASEUICR = 1;
  }
  #endif
  
  __STATIC_INLINE void nrf_nvmc_erase_all_start(NRF_NVMC_Type * p_reg)
  {
      p_reg->ERASEALL = 1;
  }
  
  #if defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
  __STATIC_INLINE void nrf_nvmc_partial_erase_duration_set(NRF_NVMC_Type * p_reg,
                                                           uint32_t        duration_ms)
  {
      p_reg->ERASEPAGEPARTIALCFG = duration_ms;
  }
  
  __STATIC_INLINE uint32_t nrf_nvmc_partial_erase_duration_get(NRF_NVMC_Type const * p_reg)
  {
      return p_reg->ERASEPAGEPARTIALCFG;
  }
  
  __STATIC_INLINE void nrf_nvmc_page_partial_erase_start(NRF_NVMC_Type * p_reg,
                                                         uint32_t        page_addr)
  {
  #if defined(NVMC_ERASEPAGEPARTIAL_ERASEPAGEPARTIAL_Msk)
      p_reg->ERASEPAGEPARTIAL = page_addr;
  #elif defined(NRF9160_XXAA)
      nrf_nvmc_page_erase_start(p_reg, page_addr);
  #else
      #error "Unknown device."
  #endif
  }
  #endif // defined(NRF_NVMC_PARTIAL_ERASE_PRESENT)
  
  #if defined(NRF_NVMC_ICACHE_PRESENT)
  __STATIC_INLINE void nrf_nvmc_icache_config_set(NRF_NVMC_Type *          p_reg,
                                                  nrf_nvmc_icache_config_t config)
  {
      p_reg->ICACHECNF = (uint32_t)config;
  }
  
  __STATIC_INLINE bool nrf_nvmc_icache_enable_check(NRF_NVMC_Type const * p_reg)
  {
      return (bool)(p_reg->ICACHECNF & NVMC_ICACHECNF_CACHEEN_Msk);
  }
  
  __STATIC_INLINE bool nrf_nvmc_icache_profiling_enable_check(NRF_NVMC_Type const * p_reg)
  {
      return (bool)(p_reg->ICACHECNF & NVMC_ICACHECNF_CACHEPROFEN_Msk);
  }
  
  __STATIC_INLINE uint32_t nrf_nvmc_icache_hit_get(NRF_NVMC_Type const * p_reg)
  {
      return p_reg->IHIT;
  }
  
  __STATIC_INLINE uint32_t nrf_nvmc_icache_miss_get(NRF_NVMC_Type const * p_reg)
  {
      return p_reg->IMISS;
  }
  
  __STATIC_INLINE void nrf_nvmc_icache_hit_miss_reset(NRF_NVMC_Type * p_reg)
  {
      p_reg->IHIT = 0;
      p_reg->IMISS = 0;
  }
  #endif // defined(NRF_NVMC_ICACHE_PRESENT)
  
  #endif // SUPPRESS_INLINE_IMPLEMENTATION
  
  /** @} */
  
  #ifdef __cplusplus
  }
  #endif
  
  #endif // NRF_NVMC_H__
  